[Debian-med-packaging] Bug#1009620: libvbz-hdf-plugin: FTBFS with glibc >= 2.34 (Catch2)

Steve Langasek steve.langasek at canonical.com
Wed Apr 13 06:54:52 BST 2022


Package: libvbz-hdf-plugin
Version: 1.0.1-2
Severity: serious
Tags: patch experimental
Justification: FTBFS
User: ubuntu-devel at lists.ubuntu.com
Usertags: origin-ubuntu jammy ubuntu-patch

Dear maintainers,

libvbz-hdf-plugin fails to build from source against glibc 2.34 or later
because MINSIGSTKSZ is no longer a compile-time constant:

[...]
In file included from /usr/include/signal.h:328,
                 from /<<PKGBUILDDIR>>/third_party/catch2/catch.hpp:5549,
                 from /<<PKGBUILDDIR>>/vbz/test/main.cpp:2:
/<<PKGBUILDDIR>>/third_party/catch2/catch.hpp:8164:58: error: call to non-‘constexpr’ function ‘long int sysconf(int)’
 8164 |     constexpr static std::size_t sigStackSize = 32768 >= MINSIGSTKSZ ? 32768 : MINSIGSTKSZ;
      |                                                          ^~~~~~~~~~~
In file included from /usr/include/x86_64-linux-gnu/bits/sigstksz.h:24,
                 from /usr/include/signal.h:328,
                 from /<<PKGBUILDDIR>>/third_party/catch2/catch.hpp:5549,
                 from /<<PKGBUILDDIR>>/vbz/test/main.cpp:2:
/usr/include/unistd.h:640:17: note: ‘long int sysconf(int)’ declared here
  640 | extern long int sysconf (int __name) __THROW;
      |                 ^~~~~~~
In file included from /<<PKGBUILDDIR>>/vbz/test/main.cpp:2:
/<<PKGBUILDDIR>>/third_party/catch2/catch.hpp:8223:45: error: size of array ‘altStackMem’ is not an integral constant-expression
 8223 |     char FatalConditionHandler::altStackMem[sigStackSize] = {};
      |                                             ^~~~~~~~~~~~
[...]

  (https://launchpad.net/ubuntu/+source/libvbz-hdf-plugin/1.0.1-2/+build/22312272)

glibc 2.34 is currently only in experimental in Debian, but this package
will need fixed to be buildable once it reaches unstable.

I've applied the attached patch in Ubuntu to fix this build failure, which
simply consists of vendorizing a new version of the Catch2 header that
includes the upstream fix for this problem.

Thanks for considering,
-- 
Steve Langasek                   Give me a lever long enough and a Free OS
Debian Developer                   to set it on, and I can move the world.
Ubuntu Developer                                   https://www.debian.org/
slangasek at ubuntu.com                                     vorlon at debian.org
-------------- next part --------------
diff -Nru libvbz-hdf-plugin-1.0.1/debian/patches/series libvbz-hdf-plugin-1.0.1/debian/patches/series
--- libvbz-hdf-plugin-1.0.1/debian/patches/series	2021-08-19 11:51:40.000000000 -0700
+++ libvbz-hdf-plugin-1.0.1/debian/patches/series	2022-04-12 22:51:08.000000000 -0700
@@ -1,3 +1,4 @@
 use-packaged-streamvbyte.patch
 fix-install-location.patch
 soversion.patch
+update-catch2.patch
diff -Nru libvbz-hdf-plugin-1.0.1/debian/patches/update-catch2.patch libvbz-hdf-plugin-1.0.1/debian/patches/update-catch2.patch
--- libvbz-hdf-plugin-1.0.1/debian/patches/update-catch2.patch	1969-12-31 16:00:00.000000000 -0800
+++ libvbz-hdf-plugin-1.0.1/debian/patches/update-catch2.patch	2022-04-12 22:51:55.000000000 -0700
@@ -0,0 +1,9996 @@
+Description: import catch2 version 2.13.8 to fix build failure with glibc 2.34
+Author: Steve Langasek <steve.langasek at ubuntu.com>
+Last-Update: 2022-04-12
+Forwarded: no
+
+Index: libvbz-hdf-plugin-1.0.1/third_party/catch2/catch.hpp
+===================================================================
+--- libvbz-hdf-plugin-1.0.1.orig/third_party/catch2/catch.hpp
++++ libvbz-hdf-plugin-1.0.1/third_party/catch2/catch.hpp
+@@ -1,9 +1,9 @@
+ /*
+- *  Catch v2.5.0
+- *  Generated: 2018-11-26 20:46:12.165372
++ *  Catch v2.13.8
++ *  Generated: 2022-01-03 21:20:09.589503
+  *  ----------------------------------------------------------
+  *  This file has been merged from multiple headers. Please don't edit it directly
+- *  Copyright (c) 2018 Two Blue Cubes Ltd. All rights reserved.
++ *  Copyright (c) 2022 Two Blue Cubes Ltd. All rights reserved.
+  *
+  *  Distributed under the Boost Software License, Version 1.0. (See accompanying
+  *  file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
+@@ -14,8 +14,8 @@
+ 
+ 
+ #define CATCH_VERSION_MAJOR 2
+-#define CATCH_VERSION_MINOR 5
+-#define CATCH_VERSION_PATCH 0
++#define CATCH_VERSION_MINOR 13
++#define CATCH_VERSION_PATCH 8
+ 
+ #ifdef __clang__
+ #    pragma clang system_header
+@@ -36,10 +36,11 @@
+ #       pragma clang diagnostic ignored "-Wcovered-switch-default"
+ #    endif
+ #elif defined __GNUC__
+-     // GCC likes to warn on REQUIREs, and we cannot suppress them
+-     // locally because g++'s support for _Pragma is lacking in older,
+-     // still supported, versions
+-#    pragma GCC diagnostic ignored "-Wparentheses"
++     // Because REQUIREs trigger GCC's -Wparentheses, and because still
++     // supported version of g++ have only buggy support for _Pragmas,
++     // Wparentheses have to be suppressed globally.
++#    pragma GCC diagnostic ignored "-Wparentheses" // See #674 for details
++
+ #    pragma GCC diagnostic push
+ #    pragma GCC diagnostic ignored "-Wunused-variable"
+ #    pragma GCC diagnostic ignored "-Wpadded"
+@@ -65,13 +66,16 @@
+ #if !defined(CATCH_CONFIG_IMPL_ONLY)
+ // start catch_platform.h
+ 
++// 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
+@@ -131,30 +135,51 @@
+ 
+ #endif
+ 
+-#if defined(CATCH_CPP17_OR_GREATER)
+-#  define CATCH_INTERNAL_CONFIG_CPP17_UNCAUGHT_EXCEPTIONS
++// 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" )
++
++#    define CATCH_INTERNAL_IGNORE_BUT_WARN(...) (void)__builtin_constant_p(__VA_ARGS__)
++
+ #endif
+ 
+-#ifdef __clang__
++#if defined(__clang__)
+ 
+-#       define CATCH_INTERNAL_SUPPRESS_GLOBALS_WARNINGS \
+-            _Pragma( "clang diagnostic push" ) \
+-            _Pragma( "clang diagnostic ignored \"-Wexit-time-destructors\"" ) \
+-            _Pragma( "clang diagnostic ignored \"-Wglobal-constructors\"")
+-#       define CATCH_INTERNAL_UNSUPPRESS_GLOBALS_WARNINGS \
+-            _Pragma( "clang diagnostic pop" )
+-
+-#       define CATCH_INTERNAL_SUPPRESS_PARENTHESES_WARNINGS \
+-            _Pragma( "clang diagnostic push" ) \
+-            _Pragma( "clang diagnostic ignored \"-Wparentheses\"" )
+-#       define CATCH_INTERNAL_UNSUPPRESS_PARENTHESES_WARNINGS \
+-            _Pragma( "clang diagnostic pop" )
+-
+-#       define CATCH_INTERNAL_SUPPRESS_UNUSED_WARNINGS \
+-            _Pragma( "clang diagnostic push" ) \
+-            _Pragma( "clang diagnostic ignored \"-Wunused-variable\"" )
+-#       define CATCH_INTERNAL_UNSUPPRESS_UNUSED_WARNINGS \
+-            _Pragma( "clang diagnostic pop" )
++#    define CATCH_INTERNAL_START_WARNINGS_SUPPRESSION _Pragma( "clang diagnostic push" )
++#    define CATCH_INTERNAL_STOP_WARNINGS_SUPPRESSION  _Pragma( "clang diagnostic pop" )
++
++// As of this writing, IBM XL's implementation of __builtin_constant_p has a bug
++// which results in calls to destructors being emitted for each temporary,
++// without a matching initialization. In practice, this can result in something
++// like `std::string::~string` being called on an uninitialized value.
++//
++// For example, this code will likely segfault under IBM XL:
++// ```
++// REQUIRE(std::string("12") + "34" == "1234")
++// ```
++//
++// Therefore, `CATCH_INTERNAL_IGNORE_BUT_WARN` is not implemented.
++#  if !defined(__ibmxl__) && !defined(__CUDACC__)
++#    define CATCH_INTERNAL_IGNORE_BUT_WARN(...) (void)__builtin_constant_p(__VA_ARGS__) /* NOLINT(cppcoreguidelines-pro-type-vararg, hicpp-vararg) */
++#  endif
++
++#    define CATCH_INTERNAL_SUPPRESS_GLOBALS_WARNINGS \
++         _Pragma( "clang diagnostic ignored \"-Wexit-time-destructors\"" ) \
++         _Pragma( "clang diagnostic ignored \"-Wglobal-constructors\"")
++
++#    define CATCH_INTERNAL_SUPPRESS_PARENTHESES_WARNINGS \
++         _Pragma( "clang diagnostic ignored \"-Wparentheses\"" )
++
++#    define CATCH_INTERNAL_SUPPRESS_UNUSED_WARNINGS \
++         _Pragma( "clang diagnostic ignored \"-Wunused-variable\"" )
++
++#    define CATCH_INTERNAL_SUPPRESS_ZERO_VARIADIC_WARNINGS \
++         _Pragma( "clang diagnostic ignored \"-Wgnu-zero-variadic-macro-arguments\"" )
++
++#    define CATCH_INTERNAL_SUPPRESS_UNUSED_TEMPLATE_WARNINGS \
++         _Pragma( "clang diagnostic ignored \"-Wunused-template\"" )
+ 
+ #endif // __clang__
+ 
+@@ -179,6 +204,7 @@
+ // Android somehow still does not support std::to_string
+ #if defined(__ANDROID__)
+ #    define CATCH_INTERNAL_CONFIG_NO_CPP11_TO_STRING
++#    define CATCH_INTERNAL_CONFIG_ANDROID_LOGWRITE
+ #endif
+ 
+ ////////////////////////////////////////////////////////////////////////////////
+@@ -203,20 +229,16 @@
+ // some versions of cygwin (most) do not support std::to_string. Use the libstd check.
+ // https://gcc.gnu.org/onlinedocs/gcc-4.8.2/libstdc++/api/a01053_source.html line 2812-2813
+ # if !((__cplusplus >= 201103L) && defined(_GLIBCXX_USE_C99) \
+-	       && !defined(_GLIBCXX_HAVE_BROKEN_VSWPRINTF))
++           && !defined(_GLIBCXX_HAVE_BROKEN_VSWPRINTF))
+ 
+-#	define CATCH_INTERNAL_CONFIG_NO_CPP11_TO_STRING
++#    define CATCH_INTERNAL_CONFIG_NO_CPP11_TO_STRING
+ 
+ # endif
+ #endif // __CYGWIN__
+ 
+ ////////////////////////////////////////////////////////////////////////////////
+ // Visual C++
+-#ifdef _MSC_VER
+-
+-#  if _MSC_VER >= 1900 // Visual Studio 2015 or newer
+-#    define CATCH_INTERNAL_CONFIG_CPP17_UNCAUGHT_EXCEPTIONS
+-#  endif
++#if defined(_MSC_VER)
+ 
+ // Universal Windows platform does not support SEH
+ // Or console colours (or console at all...)
+@@ -226,13 +248,25 @@
+ #    define CATCH_INTERNAL_CONFIG_WINDOWS_SEH
+ #  endif
+ 
++#  if !defined(__clang__) // Handle Clang masquerading for msvc
++
+ // MSVC traditional preprocessor needs some workaround for __VA_ARGS__
+ // _MSVC_TRADITIONAL == 0 means new conformant preprocessor
+ // _MSVC_TRADITIONAL == 1 means old traditional non-conformant preprocessor
+-#  if !defined(_MSVC_TRADITIONAL) || (defined(_MSVC_TRADITIONAL) && _MSVC_TRADITIONAL)
+-#    define CATCH_INTERNAL_CONFIG_TRADITIONAL_MSVC_PREPROCESSOR
+-#  endif
++#    if !defined(_MSVC_TRADITIONAL) || (defined(_MSVC_TRADITIONAL) && _MSVC_TRADITIONAL)
++#      define CATCH_INTERNAL_CONFIG_TRADITIONAL_MSVC_PREPROCESSOR
++#    endif // MSVC_TRADITIONAL
++
++// Only do this if we're not using clang on Windows, which uses `diagnostic push` & `diagnostic pop`
++#    define CATCH_INTERNAL_START_WARNINGS_SUPPRESSION __pragma( warning(push) )
++#    define CATCH_INTERNAL_STOP_WARNINGS_SUPPRESSION  __pragma( warning(pop) )
++#  endif // __clang__
++
++#endif // _MSC_VER
+ 
++#if defined(_REENTRANT) || defined(_MSC_VER)
++// Enable async processing, as -pthread is specified or no additional linking is required
++# define CATCH_INTERNAL_CONFIG_USE_ASYNC
+ #endif // _MSC_VER
+ 
+ ////////////////////////////////////////////////////////////////////////////////
+@@ -265,30 +299,56 @@
+ #endif
+ 
+ ////////////////////////////////////////////////////////////////////////////////
+-// Check if string_view is available and usable
+-// The check is split apart to work around v140 (VS2015) preprocessor issue...
+-#if defined(__has_include)
+-#if __has_include(<string_view>) && defined(CATCH_CPP17_OR_GREATER)
+-#    define CATCH_INTERNAL_CONFIG_CPP17_STRING_VIEW
++
++// RTX is a special version of Windows that is real time.
++// This means that it is detected as Windows, but does not provide
++// the same set of capabilities as real Windows does.
++#if defined(UNDER_RTSS) || defined(RTX64_BUILD)
++    #define CATCH_INTERNAL_CONFIG_NO_WINDOWS_SEH
++    #define CATCH_INTERNAL_CONFIG_NO_ASYNC
++    #define CATCH_CONFIG_COLOUR_NONE
+ #endif
++
++#if !defined(_GLIBCXX_USE_C99_MATH_TR1)
++#define CATCH_INTERNAL_CONFIG_GLOBAL_NEXTAFTER
+ #endif
+ 
+-////////////////////////////////////////////////////////////////////////////////
+-// Check if variant is available and usable
++// Various stdlib support checks that require __has_include
+ #if defined(__has_include)
+-#  if __has_include(<variant>) && defined(CATCH_CPP17_OR_GREATER)
+-#    if defined(__clang__) && (__clang_major__ < 8)
+-       // work around clang bug with libstdc++ https://bugs.llvm.org/show_bug.cgi?id=31852
+-       // fix should be in clang 8, workaround in libstdc++ 8.2
+-#      include <ciso646>
+-#      if defined(__GLIBCXX__) && defined(_GLIBCXX_RELEASE) && (_GLIBCXX_RELEASE < 9)
+-#        define CATCH_CONFIG_NO_CPP17_VARIANT
+-#     else
+-#        define CATCH_INTERNAL_CONFIG_CPP17_VARIANT
+-#      endif // defined(__GLIBCXX__) && defined(_GLIBCXX_RELEASE) && (_GLIBCXX_RELEASE < 9)
+-#    endif // defined(__clang__) && (__clang_major__ < 8)
+-#  endif // __has_include(<variant>) && defined(CATCH_CPP17_OR_GREATER)
+-#endif // __has_include
++  // Check if string_view is available and usable
++  #if __has_include(<string_view>) && defined(CATCH_CPP17_OR_GREATER)
++  #    define CATCH_INTERNAL_CONFIG_CPP17_STRING_VIEW
++  #endif
++
++  // Check if optional is available and usable
++  #  if __has_include(<optional>) && defined(CATCH_CPP17_OR_GREATER)
++  #    define CATCH_INTERNAL_CONFIG_CPP17_OPTIONAL
++  #  endif // __has_include(<optional>) && defined(CATCH_CPP17_OR_GREATER)
++
++  // Check if byte is available and usable
++  #  if __has_include(<cstddef>) && defined(CATCH_CPP17_OR_GREATER)
++  #    include <cstddef>
++  #    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)
++
++  // Check if variant is available and usable
++  #  if __has_include(<variant>) && defined(CATCH_CPP17_OR_GREATER)
++  #    if defined(__clang__) && (__clang_major__ < 8)
++         // work around clang bug with libstdc++ https://bugs.llvm.org/show_bug.cgi?id=31852
++         // fix should be in clang 8, workaround in libstdc++ 8.2
++  #      include <ciso646>
++  #      if defined(__GLIBCXX__) && defined(_GLIBCXX_RELEASE) && (_GLIBCXX_RELEASE < 9)
++  #        define CATCH_CONFIG_NO_CPP17_VARIANT
++  #      else
++  #        define CATCH_INTERNAL_CONFIG_CPP17_VARIANT
++  #      endif // defined(__GLIBCXX__) && defined(_GLIBCXX_RELEASE) && (_GLIBCXX_RELEASE < 9)
++  #    else
++  #      define CATCH_INTERNAL_CONFIG_CPP17_VARIANT
++  #    endif // defined(__clang__) && (__clang_major__ < 8)
++  #  endif // __has_include(<variant>) && defined(CATCH_CPP17_OR_GREATER)
++#endif // defined(__has_include)
+ 
+ #if defined(CATCH_INTERNAL_CONFIG_COUNTER) && !defined(CATCH_CONFIG_NO_COUNTER) && !defined(CATCH_CONFIG_COUNTER)
+ #   define CATCH_CONFIG_COUNTER
+@@ -309,8 +369,8 @@
+ #    define CATCH_CONFIG_CPP11_TO_STRING
+ #endif
+ 
+-#if defined(CATCH_INTERNAL_CONFIG_CPP17_UNCAUGHT_EXCEPTIONS) && !defined(CATCH_CONFIG_NO_CPP17_UNCAUGHT_EXCEPTIONS) && !defined(CATCH_CONFIG_CPP17_UNCAUGHT_EXCEPTIONS)
+-#  define CATCH_CONFIG_CPP17_UNCAUGHT_EXCEPTIONS
++#if defined(CATCH_INTERNAL_CONFIG_CPP17_OPTIONAL) && !defined(CATCH_CONFIG_NO_CPP17_OPTIONAL) && !defined(CATCH_CONFIG_CPP17_OPTIONAL)
++#  define CATCH_CONFIG_CPP17_OPTIONAL
+ #endif
+ 
+ #if defined(CATCH_INTERNAL_CONFIG_CPP17_STRING_VIEW) && !defined(CATCH_CONFIG_NO_CPP17_STRING_VIEW) && !defined(CATCH_CONFIG_CPP17_STRING_VIEW)
+@@ -321,6 +381,10 @@
+ #  define CATCH_CONFIG_CPP17_VARIANT
+ #endif
+ 
++#if defined(CATCH_INTERNAL_CONFIG_CPP17_BYTE) && !defined(CATCH_CONFIG_NO_CPP17_BYTE) && !defined(CATCH_CONFIG_CPP17_BYTE)
++#  define CATCH_CONFIG_CPP17_BYTE
++#endif
++
+ #if defined(CATCH_CONFIG_EXPERIMENTAL_REDIRECT)
+ #  define CATCH_INTERNAL_CONFIG_NEW_CAPTURE
+ #endif
+@@ -337,17 +401,53 @@
+ #  define CATCH_CONFIG_POLYFILL_ISNAN
+ #endif
+ 
++#if defined(CATCH_INTERNAL_CONFIG_USE_ASYNC)  && !defined(CATCH_INTERNAL_CONFIG_NO_ASYNC) && !defined(CATCH_CONFIG_NO_USE_ASYNC) && !defined(CATCH_CONFIG_USE_ASYNC)
++#  define CATCH_CONFIG_USE_ASYNC
++#endif
++
++#if defined(CATCH_INTERNAL_CONFIG_ANDROID_LOGWRITE) && !defined(CATCH_CONFIG_NO_ANDROID_LOGWRITE) && !defined(CATCH_CONFIG_ANDROID_LOGWRITE)
++#  define CATCH_CONFIG_ANDROID_LOGWRITE
++#endif
++
++#if defined(CATCH_INTERNAL_CONFIG_GLOBAL_NEXTAFTER) && !defined(CATCH_CONFIG_NO_GLOBAL_NEXTAFTER) && !defined(CATCH_CONFIG_GLOBAL_NEXTAFTER)
++#  define CATCH_CONFIG_GLOBAL_NEXTAFTER
++#endif
++
++// Even if we do not think the compiler has that warning, we still have
++// to provide a macro that can be used by the code.
++#if !defined(CATCH_INTERNAL_START_WARNINGS_SUPPRESSION)
++#   define CATCH_INTERNAL_START_WARNINGS_SUPPRESSION
++#endif
++#if !defined(CATCH_INTERNAL_STOP_WARNINGS_SUPPRESSION)
++#   define CATCH_INTERNAL_STOP_WARNINGS_SUPPRESSION
++#endif
+ #if !defined(CATCH_INTERNAL_SUPPRESS_PARENTHESES_WARNINGS)
+ #   define CATCH_INTERNAL_SUPPRESS_PARENTHESES_WARNINGS
+-#   define CATCH_INTERNAL_UNSUPPRESS_PARENTHESES_WARNINGS
+ #endif
+ #if !defined(CATCH_INTERNAL_SUPPRESS_GLOBALS_WARNINGS)
+ #   define CATCH_INTERNAL_SUPPRESS_GLOBALS_WARNINGS
+-#   define CATCH_INTERNAL_UNSUPPRESS_GLOBALS_WARNINGS
+ #endif
+ #if !defined(CATCH_INTERNAL_SUPPRESS_UNUSED_WARNINGS)
+ #   define CATCH_INTERNAL_SUPPRESS_UNUSED_WARNINGS
+-#   define CATCH_INTERNAL_UNSUPPRESS_UNUSED_WARNINGS
++#endif
++#if !defined(CATCH_INTERNAL_SUPPRESS_ZERO_VARIADIC_WARNINGS)
++#   define CATCH_INTERNAL_SUPPRESS_ZERO_VARIADIC_WARNINGS
++#endif
++
++// The goal of this macro is to avoid evaluation of the arguments, but
++// still have the compiler warn on problems inside...
++#if !defined(CATCH_INTERNAL_IGNORE_BUT_WARN)
++#   define CATCH_INTERNAL_IGNORE_BUT_WARN(...)
++#endif
++
++#if defined(__APPLE__) && defined(__apple_build_version__) && (__clang_major__ < 10)
++#   undef CATCH_INTERNAL_SUPPRESS_UNUSED_TEMPLATE_WARNINGS
++#elif defined(__clang__) && (__clang_major__ < 5)
++#   undef CATCH_INTERNAL_SUPPRESS_UNUSED_TEMPLATE_WARNINGS
++#endif
++
++#if !defined(CATCH_INTERNAL_SUPPRESS_UNUSED_TEMPLATE_WARNINGS)
++#   define CATCH_INTERNAL_SUPPRESS_UNUSED_TEMPLATE_WARNINGS
+ #endif
+ 
+ #if defined(CATCH_CONFIG_DISABLE_EXCEPTIONS)
+@@ -407,12 +507,12 @@
+             line( _line )
+         {}
+ 
+-        SourceLineInfo( SourceLineInfo const& other )        = default;
+-        SourceLineInfo( SourceLineInfo && )                  = default;
+-        SourceLineInfo& operator = ( SourceLineInfo const& ) = default;
+-        SourceLineInfo& operator = ( SourceLineInfo && )     = default;
++        SourceLineInfo( SourceLineInfo const& other )            = default;
++        SourceLineInfo& operator = ( SourceLineInfo const& )     = default;
++        SourceLineInfo( SourceLineInfo&& )              noexcept = default;
++        SourceLineInfo& operator = ( SourceLineInfo&& ) noexcept = default;
+ 
+-        bool empty() const noexcept;
++        bool empty() const noexcept { return file[0] == '\0'; }
+         bool operator == ( SourceLineInfo const& other ) const noexcept;
+         bool operator < ( SourceLineInfo const& other ) const noexcept;
+ 
+@@ -453,9 +553,10 @@
+ } // end namespace Catch
+ 
+ #define CATCH_REGISTER_TAG_ALIAS( alias, spec ) \
++    CATCH_INTERNAL_START_WARNINGS_SUPPRESSION \
+     CATCH_INTERNAL_SUPPRESS_GLOBALS_WARNINGS \
+     namespace{ Catch::RegistrarForTagAliases INTERNAL_CATCH_UNIQUE_NAME( AutoRegisterTagAlias )( alias, spec, CATCH_INTERNAL_LINEINFO ); } \
+-    CATCH_INTERNAL_UNSUPPRESS_GLOBALS_WARNINGS
++    CATCH_INTERNAL_STOP_WARNINGS_SUPPRESSION
+ 
+ // end catch_tag_alias_autoregistrar.h
+ // start catch_test_registry.h
+@@ -463,7 +564,6 @@
+ // start catch_interfaces_testcase.h
+ 
+ #include <vector>
+-#include <memory>
+ 
+ namespace Catch {
+ 
+@@ -474,8 +574,6 @@
+         virtual ~ITestInvoker();
+     };
+ 
+-    using ITestCasePtr = std::shared_ptr<ITestInvoker>;
+-
+     class TestCase;
+     struct IConfig;
+ 
+@@ -485,6 +583,7 @@
+         virtual std::vector<TestCase> const& getAllTestsSorted( IConfig const& config ) const = 0;
+     };
+ 
++    bool isThrowSafe( TestCase const& testCase, IConfig const& config );
+     bool matchTest( TestCase const& testCase, TestSpec const& testSpec, IConfig const& config );
+     std::vector<TestCase> filterTests( std::vector<TestCase> const& testCases, TestSpec const& testSpec, IConfig const& config );
+     std::vector<TestCase> const& getAllTestCasesSorted( IConfig const& config );
+@@ -497,55 +596,30 @@
+ #include <cstddef>
+ #include <string>
+ #include <iosfwd>
++#include <cassert>
+ 
+ namespace Catch {
+ 
+-    class StringData;
+-
+     /// A non-owning string class (similar to the forthcoming std::string_view)
+     /// Note that, because a StringRef may be a substring of another string,
+-    /// it may not be null terminated. c_str() must return a null terminated
+-    /// string, however, and so the StringRef will internally take ownership
+-    /// (taking a copy), if necessary. In theory this ownership is not externally
+-    /// visible - but it does mean (substring) StringRefs should not be shared between
+-    /// threads.
++    /// it may not be null terminated.
+     class StringRef {
+     public:
+         using size_type = std::size_t;
++        using const_iterator = const char*;
+ 
+     private:
+-        friend struct StringRefTestAccess;
+-
+-        char const* m_start;
+-        size_type m_size;
+-
+-        char* m_data = nullptr;
+-
+-        void takeOwnership();
+-
+         static constexpr char const* const s_empty = "";
+ 
+-    public: // construction/ assignment
+-        StringRef() noexcept
+-        :   StringRef( s_empty, 0 )
+-        {}
++        char const* m_start = s_empty;
++        size_type m_size = 0;
+ 
+-        StringRef( StringRef const& other ) noexcept
+-        :   m_start( other.m_start ),
+-            m_size( other.m_size )
+-        {}
+-
+-        StringRef( StringRef&& other ) noexcept
+-        :   m_start( other.m_start ),
+-            m_size( other.m_size ),
+-            m_data( other.m_data )
+-        {
+-            other.m_data = nullptr;
+-        }
++    public: // construction
++        constexpr StringRef() noexcept = default;
+ 
+         StringRef( char const* rawChars ) noexcept;
+ 
+-        StringRef( char const* rawChars, size_type size ) noexcept
++        constexpr StringRef( char const* rawChars, size_type size ) noexcept
+         :   m_start( rawChars ),
+             m_size( size )
+         {}
+@@ -555,99 +629,64 @@
+             m_size( stdString.size() )
+         {}
+ 
+-        ~StringRef() noexcept {
+-            delete[] m_data;
+-        }
+-
+-        auto operator = ( StringRef const &other ) noexcept -> StringRef& {
+-            delete[] m_data;
+-            m_data = nullptr;
+-            m_start = other.m_start;
+-            m_size = other.m_size;
+-            return *this;
++        explicit operator std::string() const {
++            return std::string(m_start, m_size);
+         }
+ 
+-        operator std::string() const;
+-
+-        void swap( StringRef& other ) noexcept;
+-
+     public: // operators
+         auto operator == ( StringRef const& other ) const noexcept -> bool;
+-        auto operator != ( StringRef const& other ) const noexcept -> bool;
++        auto operator != (StringRef const& other) const noexcept -> bool {
++            return !(*this == other);
++        }
+ 
+-        auto operator[] ( size_type index ) const noexcept -> char;
++        auto operator[] ( size_type index ) const noexcept -> char {
++            assert(index < m_size);
++            return m_start[index];
++        }
+ 
+     public: // named queries
+-        auto empty() const noexcept -> bool {
++        constexpr auto empty() const noexcept -> bool {
+             return m_size == 0;
+         }
+-        auto size() const noexcept -> size_type {
++        constexpr auto size() const noexcept -> size_type {
+             return m_size;
+         }
+ 
+-        auto numberOfCharacters() const noexcept -> size_type;
++        // 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
+-        auto substr( size_type start, size_type size ) const noexcept -> StringRef;
++        // Returns a substring of [start, start + length).
++        // If start + length > size(), then the substring is [start, size()).
++        // If start > size(), then the substring is empty.
++        auto substr( size_type start, size_type length ) const noexcept -> StringRef;
+ 
+-        // Returns the current start pointer.
+-        // Note that the pointer can change when if the StringRef is a substring
+-        auto currentData() const noexcept -> char const*;
++        // Returns the current start pointer. May not be null-terminated.
++        auto data() const noexcept -> char const*;
+ 
+-    private: // ownership queries - may not be consistent between calls
+-        auto isOwned() const noexcept -> bool;
+-        auto isSubstring() const noexcept -> bool;
+-    };
++        constexpr auto isNullTerminated() const noexcept -> bool {
++            return m_start[m_size] == '\0';
++        }
+ 
+-    auto operator + ( StringRef const& lhs, StringRef const& rhs ) -> std::string;
+-    auto operator + ( StringRef const& lhs, char const* rhs ) -> std::string;
+-    auto operator + ( char const* lhs, StringRef const& rhs ) -> std::string;
++    public: // iterators
++        constexpr const_iterator begin() const { return m_start; }
++        constexpr const_iterator end() const { return m_start + m_size; }
++    };
+ 
+     auto operator += ( std::string& lhs, StringRef const& sr ) -> std::string&;
+     auto operator << ( std::ostream& os, StringRef const& sr ) -> std::ostream&;
+ 
+-    inline 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
+ 
+-inline 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 );
+ }
+ 
+ // end catch_stringref.h
+-// start catch_type_traits.hpp
+-
+-
+-namespace Catch{
+-
+-#ifdef CATCH_CPP17_OR_GREATER
+-	template <typename...>
+-	inline constexpr auto is_unique = std::true_type{};
+-
+-	template <typename T, typename... Rest>
+-	inline constexpr auto is_unique<T, Rest...> = std::bool_constant<
+-		(!std::is_same_v<T, Rest> && ...) && is_unique<Rest...>
+-	>{};
+-#else
+-
+-template <typename...>
+-struct is_unique : std::true_type{};
+-
+-template <typename T0, typename T1, typename... Rest>
+-struct is_unique<T0, T1, Rest...> : std::integral_constant
+-<bool,
+-     !std::is_same<T0, T1>::value
+-     && is_unique<T0, Rest...>::value
+-     && is_unique<T1, Rest...>::value
+->{};
+-
+-#endif
+-}
+-
+-// end catch_type_traits.hpp
+ // start catch_preprocessor.hpp
+ 
+ 
+@@ -699,21 +738,224 @@
+ #define INTERNAL_CATCH_EXPAND2(...) INTERNAL_CATCH_NO## __VA_ARGS__
+ #define INTERNAL_CATCH_DEF(...) INTERNAL_CATCH_DEF __VA_ARGS__
+ #define INTERNAL_CATCH_NOINTERNAL_CATCH_DEF
++#define INTERNAL_CATCH_STRINGIZE(...) INTERNAL_CATCH_STRINGIZE2(__VA_ARGS__)
++#ifndef CATCH_CONFIG_TRADITIONAL_MSVC_PREPROCESSOR
++#define INTERNAL_CATCH_STRINGIZE2(...) #__VA_ARGS__
++#define INTERNAL_CATCH_STRINGIZE_WITHOUT_PARENS(param) INTERNAL_CATCH_STRINGIZE(INTERNAL_CATCH_REMOVE_PARENS(param))
++#else
++// MSVC is adding extra space and needs another indirection to expand INTERNAL_CATCH_NOINTERNAL_CATCH_DEF
++#define INTERNAL_CATCH_STRINGIZE2(...) INTERNAL_CATCH_STRINGIZE3(__VA_ARGS__)
++#define INTERNAL_CATCH_STRINGIZE3(...) #__VA_ARGS__
++#define INTERNAL_CATCH_STRINGIZE_WITHOUT_PARENS(param) (INTERNAL_CATCH_STRINGIZE(INTERNAL_CATCH_REMOVE_PARENS(param)) + 1)
++#endif
++
++#define INTERNAL_CATCH_MAKE_NAMESPACE2(...) ns_##__VA_ARGS__
++#define INTERNAL_CATCH_MAKE_NAMESPACE(name) INTERNAL_CATCH_MAKE_NAMESPACE2(name)
+ 
+ #define INTERNAL_CATCH_REMOVE_PARENS(...) INTERNAL_CATCH_EXPAND1(INTERNAL_CATCH_DEF __VA_ARGS__)
+ 
+-#define INTERNAL_CATCH_TEMPLATE_UNIQUE_NAME2(Name, ...) INTERNAL_CATCH_TEMPLATE_UNIQUE_NAME3(Name, __VA_ARGS__)
+ #ifndef CATCH_CONFIG_TRADITIONAL_MSVC_PREPROCESSOR
+-#define INTERNAL_CATCH_TEMPLATE_UNIQUE_NAME3(Name,...) Name " - " #__VA_ARGS__
+-#define INTERNAL_CATCH_TEMPLATE_UNIQUE_NAME(Name,...) INTERNAL_CATCH_TEMPLATE_UNIQUE_NAME2(Name, INTERNAL_CATCH_REMOVE_PARENS(__VA_ARGS__))
++#define INTERNAL_CATCH_MAKE_TYPE_LIST2(...) decltype(get_wrapper<INTERNAL_CATCH_REMOVE_PARENS_GEN(__VA_ARGS__)>())
++#define INTERNAL_CATCH_MAKE_TYPE_LIST(...) INTERNAL_CATCH_MAKE_TYPE_LIST2(INTERNAL_CATCH_REMOVE_PARENS(__VA_ARGS__))
++#else
++#define INTERNAL_CATCH_MAKE_TYPE_LIST2(...) INTERNAL_CATCH_EXPAND_VARGS(decltype(get_wrapper<INTERNAL_CATCH_REMOVE_PARENS_GEN(__VA_ARGS__)>()))
++#define INTERNAL_CATCH_MAKE_TYPE_LIST(...) INTERNAL_CATCH_EXPAND_VARGS(INTERNAL_CATCH_MAKE_TYPE_LIST2(INTERNAL_CATCH_REMOVE_PARENS(__VA_ARGS__)))
++#endif
++
++#define INTERNAL_CATCH_MAKE_TYPE_LISTS_FROM_TYPES(...)\
++    CATCH_REC_LIST(INTERNAL_CATCH_MAKE_TYPE_LIST,__VA_ARGS__)
++
++#define INTERNAL_CATCH_REMOVE_PARENS_1_ARG(_0) INTERNAL_CATCH_REMOVE_PARENS(_0)
++#define INTERNAL_CATCH_REMOVE_PARENS_2_ARG(_0, _1) INTERNAL_CATCH_REMOVE_PARENS(_0), INTERNAL_CATCH_REMOVE_PARENS_1_ARG(_1)
++#define INTERNAL_CATCH_REMOVE_PARENS_3_ARG(_0, _1, _2) INTERNAL_CATCH_REMOVE_PARENS(_0), INTERNAL_CATCH_REMOVE_PARENS_2_ARG(_1, _2)
++#define INTERNAL_CATCH_REMOVE_PARENS_4_ARG(_0, _1, _2, _3) INTERNAL_CATCH_REMOVE_PARENS(_0), INTERNAL_CATCH_REMOVE_PARENS_3_ARG(_1, _2, _3)
++#define INTERNAL_CATCH_REMOVE_PARENS_5_ARG(_0, _1, _2, _3, _4) INTERNAL_CATCH_REMOVE_PARENS(_0), INTERNAL_CATCH_REMOVE_PARENS_4_ARG(_1, _2, _3, _4)
++#define INTERNAL_CATCH_REMOVE_PARENS_6_ARG(_0, _1, _2, _3, _4, _5) INTERNAL_CATCH_REMOVE_PARENS(_0), INTERNAL_CATCH_REMOVE_PARENS_5_ARG(_1, _2, _3, _4, _5)
++#define INTERNAL_CATCH_REMOVE_PARENS_7_ARG(_0, _1, _2, _3, _4, _5, _6) INTERNAL_CATCH_REMOVE_PARENS(_0), INTERNAL_CATCH_REMOVE_PARENS_6_ARG(_1, _2, _3, _4, _5, _6)
++#define INTERNAL_CATCH_REMOVE_PARENS_8_ARG(_0, _1, _2, _3, _4, _5, _6, _7) INTERNAL_CATCH_REMOVE_PARENS(_0), INTERNAL_CATCH_REMOVE_PARENS_7_ARG(_1, _2, _3, _4, _5, _6, _7)
++#define INTERNAL_CATCH_REMOVE_PARENS_9_ARG(_0, _1, _2, _3, _4, _5, _6, _7, _8) INTERNAL_CATCH_REMOVE_PARENS(_0), INTERNAL_CATCH_REMOVE_PARENS_8_ARG(_1, _2, _3, _4, _5, _6, _7, _8)
++#define INTERNAL_CATCH_REMOVE_PARENS_10_ARG(_0, _1, _2, _3, _4, _5, _6, _7, _8, _9) INTERNAL_CATCH_REMOVE_PARENS(_0), INTERNAL_CATCH_REMOVE_PARENS_9_ARG(_1, _2, _3, _4, _5, _6, _7, _8, _9)
++#define INTERNAL_CATCH_REMOVE_PARENS_11_ARG(_0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10) INTERNAL_CATCH_REMOVE_PARENS(_0), INTERNAL_CATCH_REMOVE_PARENS_10_ARG(_1, _2, _3, _4, _5, _6, _7, _8, _9, _10)
++
++#define INTERNAL_CATCH_VA_NARGS_IMPL(_0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, N, ...) N
++
++#define INTERNAL_CATCH_TYPE_GEN\
++    template<typename...> struct TypeList {};\
++    template<typename...Ts>\
++    constexpr auto get_wrapper() noexcept -> TypeList<Ts...> { return {}; }\
++    template<template<typename...> class...> struct TemplateTypeList{};\
++    template<template<typename...> class...Cs>\
++    constexpr auto get_wrapper() noexcept -> TemplateTypeList<Cs...> { return {}; }\
++    template<typename...>\
++    struct append;\
++    template<typename...>\
++    struct rewrap;\
++    template<template<typename...> class, typename...>\
++    struct create;\
++    template<template<typename...> class, typename>\
++    struct convert;\
++    \
++    template<typename T> \
++    struct append<T> { using type = T; };\
++    template< template<typename...> class L1, typename...E1, template<typename...> class L2, typename...E2, typename...Rest>\
++    struct append<L1<E1...>, L2<E2...>, Rest...> { using type = typename append<L1<E1...,E2...>, Rest...>::type; };\
++    template< template<typename...> class L1, typename...E1, typename...Rest>\
++    struct append<L1<E1...>, TypeList<mpl_::na>, Rest...> { using type = L1<E1...>; };\
++    \
++    template< template<typename...> class Container, template<typename...> class List, typename...elems>\
++    struct rewrap<TemplateTypeList<Container>, List<elems...>> { using type = TypeList<Container<elems...>>; };\
++    template< template<typename...> class Container, template<typename...> class List, class...Elems, typename...Elements>\
++    struct rewrap<TemplateTypeList<Container>, List<Elems...>, Elements...> { using type = typename append<TypeList<Container<Elems...>>, typename rewrap<TemplateTypeList<Container>, Elements...>::type>::type; };\
++    \
++    template<template <typename...> class Final, template< typename...> class...Containers, typename...Types>\
++    struct create<Final, TemplateTypeList<Containers...>, TypeList<Types...>> { using type = typename append<Final<>, typename rewrap<TemplateTypeList<Containers>, Types...>::type...>::type; };\
++    template<template <typename...> class Final, template <typename...> class List, typename...Ts>\
++    struct convert<Final, List<Ts...>> { using type = typename append<Final<>,TypeList<Ts>...>::type; };
++
++#define INTERNAL_CATCH_NTTP_1(signature, ...)\
++    template<INTERNAL_CATCH_REMOVE_PARENS(signature)> struct Nttp{};\
++    template<INTERNAL_CATCH_REMOVE_PARENS(signature)>\
++    constexpr auto get_wrapper() noexcept -> Nttp<__VA_ARGS__> { return {}; } \
++    template<template<INTERNAL_CATCH_REMOVE_PARENS(signature)> class...> struct NttpTemplateTypeList{};\
++    template<template<INTERNAL_CATCH_REMOVE_PARENS(signature)> class...Cs>\
++    constexpr auto get_wrapper() noexcept -> NttpTemplateTypeList<Cs...> { return {}; } \
++    \
++    template< template<INTERNAL_CATCH_REMOVE_PARENS(signature)> class Container, template<INTERNAL_CATCH_REMOVE_PARENS(signature)> class List, INTERNAL_CATCH_REMOVE_PARENS(signature)>\
++    struct rewrap<NttpTemplateTypeList<Container>, List<__VA_ARGS__>> { using type = TypeList<Container<__VA_ARGS__>>; };\
++    template< template<INTERNAL_CATCH_REMOVE_PARENS(signature)> class Container, template<INTERNAL_CATCH_REMOVE_PARENS(signature)> class List, INTERNAL_CATCH_REMOVE_PARENS(signature), typename...Elements>\
++    struct rewrap<NttpTemplateTypeList<Container>, List<__VA_ARGS__>, Elements...> { using type = typename append<TypeList<Container<__VA_ARGS__>>, typename rewrap<NttpTemplateTypeList<Container>, Elements...>::type>::type; };\
++    template<template <typename...> class Final, template<INTERNAL_CATCH_REMOVE_PARENS(signature)> class...Containers, typename...Types>\
++    struct create<Final, NttpTemplateTypeList<Containers...>, TypeList<Types...>> { using type = typename append<Final<>, typename rewrap<NttpTemplateTypeList<Containers>, Types...>::type...>::type; };
++
++#define INTERNAL_CATCH_DECLARE_SIG_TEST0(TestName)
++#define INTERNAL_CATCH_DECLARE_SIG_TEST1(TestName, signature)\
++    template<INTERNAL_CATCH_REMOVE_PARENS(signature)>\
++    static void TestName()
++#define INTERNAL_CATCH_DECLARE_SIG_TEST_X(TestName, signature, ...)\
++    template<INTERNAL_CATCH_REMOVE_PARENS(signature)>\
++    static void TestName()
++
++#define INTERNAL_CATCH_DEFINE_SIG_TEST0(TestName)
++#define INTERNAL_CATCH_DEFINE_SIG_TEST1(TestName, signature)\
++    template<INTERNAL_CATCH_REMOVE_PARENS(signature)>\
++    static void TestName()
++#define INTERNAL_CATCH_DEFINE_SIG_TEST_X(TestName, signature,...)\
++    template<INTERNAL_CATCH_REMOVE_PARENS(signature)>\
++    static void TestName()
++
++#define INTERNAL_CATCH_NTTP_REGISTER0(TestFunc, signature)\
++    template<typename Type>\
++    void reg_test(TypeList<Type>, Catch::NameAndTags nameAndTags)\
++    {\
++        Catch::AutoReg( Catch::makeTestInvoker(&TestFunc<Type>), CATCH_INTERNAL_LINEINFO, Catch::StringRef(), nameAndTags);\
++    }
++
++#define INTERNAL_CATCH_NTTP_REGISTER(TestFunc, signature, ...)\
++    template<INTERNAL_CATCH_REMOVE_PARENS(signature)>\
++    void reg_test(Nttp<__VA_ARGS__>, Catch::NameAndTags nameAndTags)\
++    {\
++        Catch::AutoReg( Catch::makeTestInvoker(&TestFunc<__VA_ARGS__>), CATCH_INTERNAL_LINEINFO, Catch::StringRef(), nameAndTags);\
++    }
++
++#define INTERNAL_CATCH_NTTP_REGISTER_METHOD0(TestName, signature, ...)\
++    template<typename Type>\
++    void reg_test(TypeList<Type>, Catch::StringRef className, Catch::NameAndTags nameAndTags)\
++    {\
++        Catch::AutoReg( Catch::makeTestInvoker(&TestName<Type>::test), CATCH_INTERNAL_LINEINFO, className, nameAndTags);\
++    }
++
++#define INTERNAL_CATCH_NTTP_REGISTER_METHOD(TestName, signature, ...)\
++    template<INTERNAL_CATCH_REMOVE_PARENS(signature)>\
++    void reg_test(Nttp<__VA_ARGS__>, Catch::StringRef className, Catch::NameAndTags nameAndTags)\
++    {\
++        Catch::AutoReg( Catch::makeTestInvoker(&TestName<__VA_ARGS__>::test), CATCH_INTERNAL_LINEINFO, className, nameAndTags);\
++    }
++
++#define INTERNAL_CATCH_DECLARE_SIG_TEST_METHOD0(TestName, ClassName)
++#define INTERNAL_CATCH_DECLARE_SIG_TEST_METHOD1(TestName, ClassName, signature)\
++    template<typename TestType> \
++    struct TestName : INTERNAL_CATCH_REMOVE_PARENS(ClassName)<TestType> { \
++        void test();\
++    }
++
++#define INTERNAL_CATCH_DECLARE_SIG_TEST_METHOD_X(TestName, ClassName, signature, ...)\
++    template<INTERNAL_CATCH_REMOVE_PARENS(signature)> \
++    struct TestName : INTERNAL_CATCH_REMOVE_PARENS(ClassName)<__VA_ARGS__> { \
++        void test();\
++    }
++
++#define INTERNAL_CATCH_DEFINE_SIG_TEST_METHOD0(TestName)
++#define INTERNAL_CATCH_DEFINE_SIG_TEST_METHOD1(TestName, signature)\
++    template<typename TestType> \
++    void INTERNAL_CATCH_MAKE_NAMESPACE(TestName)::TestName<TestType>::test()
++#define INTERNAL_CATCH_DEFINE_SIG_TEST_METHOD_X(TestName, signature, ...)\
++    template<INTERNAL_CATCH_REMOVE_PARENS(signature)> \
++    void INTERNAL_CATCH_MAKE_NAMESPACE(TestName)::TestName<__VA_ARGS__>::test()
++
++#ifndef CATCH_CONFIG_TRADITIONAL_MSVC_PREPROCESSOR
++#define INTERNAL_CATCH_NTTP_0
++#define INTERNAL_CATCH_NTTP_GEN(...) INTERNAL_CATCH_VA_NARGS_IMPL(__VA_ARGS__, INTERNAL_CATCH_NTTP_1(__VA_ARGS__), INTERNAL_CATCH_NTTP_1(__VA_ARGS__), INTERNAL_CATCH_NTTP_1(__VA_ARGS__), INTERNAL_CATCH_NTTP_1(__VA_ARGS__), INTERNAL_CATCH_NTTP_1(__VA_ARGS__), INTERNAL_CATCH_NTTP_1( __VA_ARGS__), INTERNAL_CATCH_NTTP_1( __VA_ARGS__), INTERNAL_CATCH_NTTP_1( __VA_ARGS__), INTERNAL_CATCH_NTTP_1( __VA_ARGS__),INTERNAL_CATCH_NTTP_1( __VA_ARGS__), INTERNAL_CATCH_NTTP_0)
++#define INTERNAL_CATCH_DEFINE_SIG_TEST_METHOD(TestName, ...) INTERNAL_CATCH_VA_NARGS_IMPL( "dummy", __VA_ARGS__, INTERNAL_CATCH_DEFINE_SIG_TEST_METHOD_X,INTERNAL_CATCH_DEFINE_SIG_TEST_METHOD_X, INTERNAL_CATCH_DEFINE_SIG_TEST_METHOD_X, INTERNAL_CATCH_DEFINE_SIG_TEST_METHOD_X, INTERNAL_CATCH_DEFINE_SIG_TEST_METHOD_X, INTERNAL_CATCH_DEFINE_SIG_TEST_METHOD_X, INTERNAL_CATCH_DEFINE_SIG_TEST_METHOD_X,INTERNAL_CATCH_DEFINE_SIG_TEST_METHOD_X,INTERNAL_CATCH_DEFINE_SIG_TEST_METHOD_X, INTERNAL_CATCH_DEFINE_SIG_TEST_METHOD1, INTERNAL_CATCH_DEFINE_SIG_TEST_METHOD0)(TestName, __VA_ARGS__)
++#define INTERNAL_CATCH_DECLARE_SIG_TEST_METHOD(TestName, ClassName, ...) INTERNAL_CATCH_VA_NARGS_IMPL( "dummy", __VA_ARGS__, INTERNAL_CATCH_DECLARE_SIG_TEST_METHOD_X,INTERNAL_CATCH_DECLARE_SIG_TEST_METHOD_X, INTERNAL_CATCH_DECLARE_SIG_TEST_METHOD_X, INTERNAL_CATCH_DECLARE_SIG_TEST_METHOD_X, INTERNAL_CATCH_DECLARE_SIG_TEST_METHOD_X, INTERNAL_CATCH_DECLARE_SIG_TEST_METHOD_X, INTERNAL_CATCH_DECLARE_SIG_TEST_METHOD_X,INTERNAL_CATCH_DECLARE_SIG_TEST_METHOD_X,INTERNAL_CATCH_DECLARE_SIG_TEST_METHOD_X, INTERNAL_CATCH_DECLARE_SIG_TEST_METHOD1, INTERNAL_CATCH_DECLARE_SIG_TEST_METHOD0)(TestName, ClassName, __VA_ARGS__)
++#define INTERNAL_CATCH_NTTP_REG_METHOD_GEN(TestName, ...) INTERNAL_CATCH_VA_NARGS_IMPL( "dummy", __VA_ARGS__, INTERNAL_CATCH_NTTP_REGISTER_METHOD, INTERNAL_CATCH_NTTP_REGISTER_METHOD, INTERNAL_CATCH_NTTP_REGISTER_METHOD, INTERNAL_CATCH_NTTP_REGISTER_METHOD, INTERNAL_CATCH_NTTP_REGISTER_METHOD, INTERNAL_CATCH_NTTP_REGISTER_METHOD, INTERNAL_CATCH_NTTP_REGISTER_METHOD, INTERNAL_CATCH_NTTP_REGISTER_METHOD, INTERNAL_CATCH_NTTP_REGISTER_METHOD, INTERNAL_CATCH_NTTP_REGISTER_METHOD0, INTERNAL_CATCH_NTTP_REGISTER_METHOD0)(TestName, __VA_ARGS__)
++#define INTERNAL_CATCH_NTTP_REG_GEN(TestFunc, ...) INTERNAL_CATCH_VA_NARGS_IMPL( "dummy", __VA_ARGS__, INTERNAL_CATCH_NTTP_REGISTER, INTERNAL_CATCH_NTTP_REGISTER, INTERNAL_CATCH_NTTP_REGISTER, INTERNAL_CATCH_NTTP_REGISTER, INTERNAL_CATCH_NTTP_REGISTER, INTERNAL_CATCH_NTTP_REGISTER, INTERNAL_CATCH_NTTP_REGISTER, INTERNAL_CATCH_NTTP_REGISTER, INTERNAL_CATCH_NTTP_REGISTER, INTERNAL_CATCH_NTTP_REGISTER0, INTERNAL_CATCH_NTTP_REGISTER0)(TestFunc, __VA_ARGS__)
++#define INTERNAL_CATCH_DEFINE_SIG_TEST(TestName, ...) INTERNAL_CATCH_VA_NARGS_IMPL( "dummy", __VA_ARGS__, INTERNAL_CATCH_DEFINE_SIG_TEST_X, INTERNAL_CATCH_DEFINE_SIG_TEST_X, INTERNAL_CATCH_DEFINE_SIG_TEST_X, INTERNAL_CATCH_DEFINE_SIG_TEST_X, INTERNAL_CATCH_DEFINE_SIG_TEST_X, INTERNAL_CATCH_DEFINE_SIG_TEST_X, INTERNAL_CATCH_DEFINE_SIG_TEST_X, INTERNAL_CATCH_DEFINE_SIG_TEST_X,INTERNAL_CATCH_DEFINE_SIG_TEST_X,INTERNAL_CATCH_DEFINE_SIG_TEST1, INTERNAL_CATCH_DEFINE_SIG_TEST0)(TestName, __VA_ARGS__)
++#define INTERNAL_CATCH_DECLARE_SIG_TEST(TestName, ...) INTERNAL_CATCH_VA_NARGS_IMPL( "dummy", __VA_ARGS__, INTERNAL_CATCH_DECLARE_SIG_TEST_X,INTERNAL_CATCH_DECLARE_SIG_TEST_X, INTERNAL_CATCH_DECLARE_SIG_TEST_X, INTERNAL_CATCH_DECLARE_SIG_TEST_X, INTERNAL_CATCH_DECLARE_SIG_TEST_X, INTERNAL_CATCH_DECLARE_SIG_TEST_X, INTERNAL_CATCH_DEFINE_SIG_TEST_X,INTERNAL_CATCH_DECLARE_SIG_TEST_X,INTERNAL_CATCH_DECLARE_SIG_TEST_X, INTERNAL_CATCH_DECLARE_SIG_TEST1, INTERNAL_CATCH_DECLARE_SIG_TEST0)(TestName, __VA_ARGS__)
++#define INTERNAL_CATCH_REMOVE_PARENS_GEN(...) INTERNAL_CATCH_VA_NARGS_IMPL(__VA_ARGS__, INTERNAL_CATCH_REMOVE_PARENS_11_ARG,INTERNAL_CATCH_REMOVE_PARENS_10_ARG,INTERNAL_CATCH_REMOVE_PARENS_9_ARG,INTERNAL_CATCH_REMOVE_PARENS_8_ARG,INTERNAL_CATCH_REMOVE_PARENS_7_ARG,INTERNAL_CATCH_REMOVE_PARENS_6_ARG,INTERNAL_CATCH_REMOVE_PARENS_5_ARG,INTERNAL_CATCH_REMOVE_PARENS_4_ARG,INTERNAL_CATCH_REMOVE_PARENS_3_ARG,INTERNAL_CATCH_REMOVE_PARENS_2_ARG,INTERNAL_CATCH_REMOVE_PARENS_1_ARG)(__VA_ARGS__)
+ #else
+-// MSVC is adding extra space and needs more calls to properly remove ()
+-#define INTERNAL_CATCH_TEMPLATE_UNIQUE_NAME3(Name,...) Name " -" #__VA_ARGS__
+-#define INTERNAL_CATCH_TEMPLATE_UNIQUE_NAME1(Name, ...) INTERNAL_CATCH_TEMPLATE_UNIQUE_NAME2(Name, __VA_ARGS__)
+-#define INTERNAL_CATCH_TEMPLATE_UNIQUE_NAME(Name, ...) INTERNAL_CATCH_TEMPLATE_UNIQUE_NAME1(Name, INTERNAL_CATCH_EXPAND_VARGS(INTERNAL_CATCH_REMOVE_PARENS(__VA_ARGS__)))
++#define INTERNAL_CATCH_NTTP_0(signature)
++#define INTERNAL_CATCH_NTTP_GEN(...) INTERNAL_CATCH_EXPAND_VARGS(INTERNAL_CATCH_VA_NARGS_IMPL(__VA_ARGS__, INTERNAL_CATCH_NTTP_1, INTERNAL_CATCH_NTTP_1, INTERNAL_CATCH_NTTP_1, INTERNAL_CATCH_NTTP_1, INTERNAL_CATCH_NTTP_1, INTERNAL_CATCH_NTTP_1, INTERNAL_CATCH_NTTP_1, INTERNAL_CATCH_NTTP_1, INTERNAL_CATCH_NTTP_1,INTERNAL_CATCH_NTTP_1, INTERNAL_CATCH_NTTP_0)( __VA_ARGS__))
++#define INTERNAL_CATCH_DEFINE_SIG_TEST_METHOD(TestName, ...) INTERNAL_CATCH_EXPAND_VARGS(INTERNAL_CATCH_VA_NARGS_IMPL( "dummy", __VA_ARGS__, INTERNAL_CATCH_DEFINE_SIG_TEST_METHOD_X,INTERNAL_CATCH_DEFINE_SIG_TEST_METHOD_X, INTERNAL_CATCH_DEFINE_SIG_TEST_METHOD_X, INTERNAL_CATCH_DEFINE_SIG_TEST_METHOD_X, INTERNAL_CATCH_DEFINE_SIG_TEST_METHOD_X, INTERNAL_CATCH_DEFINE_SIG_TEST_METHOD_X, INTERNAL_CATCH_DEFINE_SIG_TEST_METHOD_X,INTERNAL_CATCH_DEFINE_SIG_TEST_METHOD_X,INTERNAL_CATCH_DEFINE_SIG_TEST_METHOD_X, INTERNAL_CATCH_DEFINE_SIG_TEST_METHOD1, INTERNAL_CATCH_DEFINE_SIG_TEST_METHOD0)(TestName, __VA_ARGS__))
++#define INTERNAL_CATCH_DECLARE_SIG_TEST_METHOD(TestName, ClassName, ...) INTERNAL_CATCH_EXPAND_VARGS(INTERNAL_CATCH_VA_NARGS_IMPL( "dummy", __VA_ARGS__, INTERNAL_CATCH_DECLARE_SIG_TEST_METHOD_X,INTERNAL_CATCH_DECLARE_SIG_TEST_METHOD_X, INTERNAL_CATCH_DECLARE_SIG_TEST_METHOD_X, INTERNAL_CATCH_DECLARE_SIG_TEST_METHOD_X, INTERNAL_CATCH_DECLARE_SIG_TEST_METHOD_X, INTERNAL_CATCH_DECLARE_SIG_TEST_METHOD_X, INTERNAL_CATCH_DECLARE_SIG_TEST_METHOD_X,INTERNAL_CATCH_DECLARE_SIG_TEST_METHOD_X,INTERNAL_CATCH_DECLARE_SIG_TEST_METHOD_X, INTERNAL_CATCH_DECLARE_SIG_TEST_METHOD1, INTERNAL_CATCH_DECLARE_SIG_TEST_METHOD0)(TestName, ClassName, __VA_ARGS__))
++#define INTERNAL_CATCH_NTTP_REG_METHOD_GEN(TestName, ...) INTERNAL_CATCH_EXPAND_VARGS(INTERNAL_CATCH_VA_NARGS_IMPL( "dummy", __VA_ARGS__, INTERNAL_CATCH_NTTP_REGISTER_METHOD, INTERNAL_CATCH_NTTP_REGISTER_METHOD, INTERNAL_CATCH_NTTP_REGISTER_METHOD, INTERNAL_CATCH_NTTP_REGISTER_METHOD, INTERNAL_CATCH_NTTP_REGISTER_METHOD, INTERNAL_CATCH_NTTP_REGISTER_METHOD, INTERNAL_CATCH_NTTP_REGISTER_METHOD, INTERNAL_CATCH_NTTP_REGISTER_METHOD, INTERNAL_CATCH_NTTP_REGISTER_METHOD, INTERNAL_CATCH_NTTP_REGISTER_METHOD0, INTERNAL_CATCH_NTTP_REGISTER_METHOD0)(TestName, __VA_ARGS__))
++#define INTERNAL_CATCH_NTTP_REG_GEN(TestFunc, ...) INTERNAL_CATCH_EXPAND_VARGS(INTERNAL_CATCH_VA_NARGS_IMPL( "dummy", __VA_ARGS__, INTERNAL_CATCH_NTTP_REGISTER, INTERNAL_CATCH_NTTP_REGISTER, INTERNAL_CATCH_NTTP_REGISTER, INTERNAL_CATCH_NTTP_REGISTER, INTERNAL_CATCH_NTTP_REGISTER, INTERNAL_CATCH_NTTP_REGISTER, INTERNAL_CATCH_NTTP_REGISTER, INTERNAL_CATCH_NTTP_REGISTER, INTERNAL_CATCH_NTTP_REGISTER, INTERNAL_CATCH_NTTP_REGISTER0, INTERNAL_CATCH_NTTP_REGISTER0)(TestFunc, __VA_ARGS__))
++#define INTERNAL_CATCH_DEFINE_SIG_TEST(TestName, ...) INTERNAL_CATCH_EXPAND_VARGS(INTERNAL_CATCH_VA_NARGS_IMPL( "dummy", __VA_ARGS__, INTERNAL_CATCH_DEFINE_SIG_TEST_X, INTERNAL_CATCH_DEFINE_SIG_TEST_X, INTERNAL_CATCH_DEFINE_SIG_TEST_X, INTERNAL_CATCH_DEFINE_SIG_TEST_X, INTERNAL_CATCH_DEFINE_SIG_TEST_X, INTERNAL_CATCH_DEFINE_SIG_TEST_X, INTERNAL_CATCH_DEFINE_SIG_TEST_X, INTERNAL_CATCH_DEFINE_SIG_TEST_X,INTERNAL_CATCH_DEFINE_SIG_TEST_X,INTERNAL_CATCH_DEFINE_SIG_TEST1, INTERNAL_CATCH_DEFINE_SIG_TEST0)(TestName, __VA_ARGS__))
++#define INTERNAL_CATCH_DECLARE_SIG_TEST(TestName, ...) INTERNAL_CATCH_EXPAND_VARGS(INTERNAL_CATCH_VA_NARGS_IMPL( "dummy", __VA_ARGS__, INTERNAL_CATCH_DECLARE_SIG_TEST_X,INTERNAL_CATCH_DECLARE_SIG_TEST_X, INTERNAL_CATCH_DECLARE_SIG_TEST_X, INTERNAL_CATCH_DECLARE_SIG_TEST_X, INTERNAL_CATCH_DECLARE_SIG_TEST_X, INTERNAL_CATCH_DECLARE_SIG_TEST_X, INTERNAL_CATCH_DEFINE_SIG_TEST_X,INTERNAL_CATCH_DECLARE_SIG_TEST_X,INTERNAL_CATCH_DECLARE_SIG_TEST_X, INTERNAL_CATCH_DECLARE_SIG_TEST1, INTERNAL_CATCH_DECLARE_SIG_TEST0)(TestName, __VA_ARGS__))
++#define INTERNAL_CATCH_REMOVE_PARENS_GEN(...) INTERNAL_CATCH_EXPAND_VARGS(INTERNAL_CATCH_VA_NARGS_IMPL(__VA_ARGS__, INTERNAL_CATCH_REMOVE_PARENS_11_ARG,INTERNAL_CATCH_REMOVE_PARENS_10_ARG,INTERNAL_CATCH_REMOVE_PARENS_9_ARG,INTERNAL_CATCH_REMOVE_PARENS_8_ARG,INTERNAL_CATCH_REMOVE_PARENS_7_ARG,INTERNAL_CATCH_REMOVE_PARENS_6_ARG,INTERNAL_CATCH_REMOVE_PARENS_5_ARG,INTERNAL_CATCH_REMOVE_PARENS_4_ARG,INTERNAL_CATCH_REMOVE_PARENS_3_ARG,INTERNAL_CATCH_REMOVE_PARENS_2_ARG,INTERNAL_CATCH_REMOVE_PARENS_1_ARG)(__VA_ARGS__))
+ #endif
+ 
+ // end catch_preprocessor.hpp
++// start catch_meta.hpp
++
++
++#include <type_traits>
++
++namespace Catch {
++    template<typename T>
++    struct always_false : std::false_type {};
++
++    template <typename> struct true_given : std::true_type {};
++    struct is_callable_tester {
++        template <typename Fun, typename... Args>
++        true_given<decltype(std::declval<Fun>()(std::declval<Args>()...))> static test(int);
++        template <typename...>
++        std::false_type static test(...);
++    };
++
++    template <typename T>
++    struct is_callable;
++
++    template <typename Fun, typename... Args>
++    struct is_callable<Fun(Args...)> : decltype(is_callable_tester::test<Fun, Args...>(0)) {};
++
++#if defined(__cpp_lib_is_invocable) && __cpp_lib_is_invocable >= 201703
++    // std::result_of is deprecated in C++17 and removed in C++20. Hence, it is
++    // replaced with std::invoke_result here.
++    template <typename Func, typename... U>
++    using FunctionReturnType = std::remove_reference_t<std::remove_cv_t<std::invoke_result_t<Func, U...>>>;
++#else
++    // Keep ::type here because we still support C++11
++    template <typename Func, typename... U>
++    using FunctionReturnType = typename std::remove_reference<typename std::remove_cv<typename std::result_of<Func(U...)>::type>::type>::type;
++#endif
++
++} // namespace Catch
++
++namespace mpl_{
++    struct na;
++}
++
++// end catch_meta.hpp
+ namespace Catch {
+ 
+ template<typename C>
+@@ -758,38 +1000,70 @@
+             };                            \
+         }                                 \
+         void TestName::test()
+-    #define INTERNAL_CATCH_TEMPLATE_TEST_CASE_NO_REGISTRATION( TestName, ... )  \
+-        template<typename TestType>                                             \
+-        static void TestName()
+-    #define INTERNAL_CATCH_TEMPLATE_TEST_CASE_METHOD_NO_REGISTRATION( TestName, ClassName, ... )    \
++    #define INTERNAL_CATCH_TEMPLATE_TEST_CASE_NO_REGISTRATION_2( TestName, TestFunc, Name, Tags, Signature, ... )  \
++        INTERNAL_CATCH_DEFINE_SIG_TEST(TestFunc, INTERNAL_CATCH_REMOVE_PARENS(Signature))
++    #define INTERNAL_CATCH_TEMPLATE_TEST_CASE_METHOD_NO_REGISTRATION_2( TestNameClass, TestName, ClassName, Name, Tags, Signature, ... )    \
+         namespace{                                                                                  \
+-            template<typename TestType>                                                             \
+-            struct TestName : INTERNAL_CATCH_REMOVE_PARENS(ClassName <TestType>) {     \
+-                void test();                                                                        \
+-            };                                                                                      \
++            namespace INTERNAL_CATCH_MAKE_NAMESPACE(TestName) {                                      \
++            INTERNAL_CATCH_DECLARE_SIG_TEST_METHOD(TestName, ClassName, INTERNAL_CATCH_REMOVE_PARENS(Signature));\
+         }                                                                                           \
+-        template<typename TestType>                                                                 \
+-        void TestName::test()
++        }                                                                                           \
++        INTERNAL_CATCH_DEFINE_SIG_TEST_METHOD(TestName, INTERNAL_CATCH_REMOVE_PARENS(Signature))
++
++    #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__ )
++    #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__ ) )
++    #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__ )
++    #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__ ) )
++    #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__ )
++    #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__ ) )
++    #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__ )
++    #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__ ) )
++    #endif
+ #endif
+ 
+     ///////////////////////////////////////////////////////////////////////////////
+     #define INTERNAL_CATCH_TESTCASE2( TestName, ... ) \
+         static void TestName(); \
++        CATCH_INTERNAL_START_WARNINGS_SUPPRESSION \
+         CATCH_INTERNAL_SUPPRESS_GLOBALS_WARNINGS \
+         namespace{ Catch::AutoReg INTERNAL_CATCH_UNIQUE_NAME( autoRegistrar )( Catch::makeTestInvoker( &TestName ), CATCH_INTERNAL_LINEINFO, Catch::StringRef(), Catch::NameAndTags{ __VA_ARGS__ } ); } /* NOLINT */ \
+-        CATCH_INTERNAL_UNSUPPRESS_GLOBALS_WARNINGS \
++        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( C_A_T_C_H_T_E_S_T_ ), __VA_ARGS__ )
+ 
+     ///////////////////////////////////////////////////////////////////////////////
+     #define INTERNAL_CATCH_METHOD_AS_TEST_CASE( QualifiedMethod, ... ) \
++        CATCH_INTERNAL_START_WARNINGS_SUPPRESSION \
+         CATCH_INTERNAL_SUPPRESS_GLOBALS_WARNINGS \
+         namespace{ Catch::AutoReg INTERNAL_CATCH_UNIQUE_NAME( autoRegistrar )( Catch::makeTestInvoker( &QualifiedMethod ), CATCH_INTERNAL_LINEINFO, "&" #QualifiedMethod, Catch::NameAndTags{ __VA_ARGS__ } ); } /* NOLINT */ \
+-        CATCH_INTERNAL_UNSUPPRESS_GLOBALS_WARNINGS
++        CATCH_INTERNAL_STOP_WARNINGS_SUPPRESSION
+ 
+     ///////////////////////////////////////////////////////////////////////////////
+     #define INTERNAL_CATCH_TEST_CASE_METHOD2( TestName, ClassName, ... )\
++        CATCH_INTERNAL_START_WARNINGS_SUPPRESSION \
+         CATCH_INTERNAL_SUPPRESS_GLOBALS_WARNINGS \
+         namespace{ \
+             struct TestName : INTERNAL_CATCH_REMOVE_PARENS(ClassName) { \
+@@ -797,88 +1071,273 @@
+             }; \
+             Catch::AutoReg INTERNAL_CATCH_UNIQUE_NAME( autoRegistrar ) ( Catch::makeTestInvoker( &TestName::test ), CATCH_INTERNAL_LINEINFO, #ClassName, Catch::NameAndTags{ __VA_ARGS__ } ); /* NOLINT */ \
+         } \
+-        CATCH_INTERNAL_UNSUPPRESS_GLOBALS_WARNINGS \
++        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( C_A_T_C_H_T_E_S_T_ ), ClassName, __VA_ARGS__ )
+ 
+     ///////////////////////////////////////////////////////////////////////////////
+     #define INTERNAL_CATCH_REGISTER_TESTCASE( Function, ... ) \
++        CATCH_INTERNAL_START_WARNINGS_SUPPRESSION \
+         CATCH_INTERNAL_SUPPRESS_GLOBALS_WARNINGS \
+         Catch::AutoReg INTERNAL_CATCH_UNIQUE_NAME( autoRegistrar )( Catch::makeTestInvoker( Function ), CATCH_INTERNAL_LINEINFO, Catch::StringRef(), Catch::NameAndTags{ __VA_ARGS__ } ); /* NOLINT */ \
+-        CATCH_INTERNAL_UNSUPPRESS_GLOBALS_WARNINGS
++        CATCH_INTERNAL_STOP_WARNINGS_SUPPRESSION
+ 
+     ///////////////////////////////////////////////////////////////////////////////
+-    #define INTERNAL_CATCH_TEMPLATE_TEST_CASE_2(TestName, TestFunc, Name, Tags, ... )\
++    #define INTERNAL_CATCH_TEMPLATE_TEST_CASE_2(TestName, TestFunc, Name, Tags, Signature, ... )\
++        CATCH_INTERNAL_START_WARNINGS_SUPPRESSION \
+         CATCH_INTERNAL_SUPPRESS_GLOBALS_WARNINGS \
+-        template<typename TestType> \
+-        static void TestFunc();\
++        CATCH_INTERNAL_SUPPRESS_ZERO_VARIADIC_WARNINGS \
++        CATCH_INTERNAL_SUPPRESS_UNUSED_TEMPLATE_WARNINGS \
++        INTERNAL_CATCH_DECLARE_SIG_TEST(TestFunc, INTERNAL_CATCH_REMOVE_PARENS(Signature));\
+         namespace {\
++        namespace INTERNAL_CATCH_MAKE_NAMESPACE(TestName){\
++            INTERNAL_CATCH_TYPE_GEN\
++            INTERNAL_CATCH_NTTP_GEN(INTERNAL_CATCH_REMOVE_PARENS(Signature))\
++            INTERNAL_CATCH_NTTP_REG_GEN(TestFunc,INTERNAL_CATCH_REMOVE_PARENS(Signature))\
+             template<typename...Types> \
+             struct TestName{\
+-                template<typename...Ts> \
+-                TestName(Ts...names){\
+-                    CATCH_INTERNAL_CHECK_UNIQUE_TYPES(CATCH_REC_LIST(INTERNAL_CATCH_REMOVE_PARENS, __VA_ARGS__)) \
++                TestName(){\
++                    int index = 0;                                    \
++                    constexpr char const* tmpl_types[] = {CATCH_REC_LIST(INTERNAL_CATCH_STRINGIZE_WITHOUT_PARENS, __VA_ARGS__)};\
+                     using expander = int[];\
+-                    (void)expander{(Catch::AutoReg( Catch::makeTestInvoker( &TestFunc<Types> ), CATCH_INTERNAL_LINEINFO, Catch::StringRef(), Catch::NameAndTags{ names, Tags } ), 0)... };/* NOLINT */ \
++                    (void)expander{(reg_test(Types{}, Catch::NameAndTags{ Name " - " + std::string(tmpl_types[index]), Tags } ), index++)... };/* NOLINT */ \
+                 }\
+             };\
+-            INTERNAL_CATCH_TEMPLATE_REGISTRY_INITIATE(TestName, Name, __VA_ARGS__) \
++            static int INTERNAL_CATCH_UNIQUE_NAME( globalRegistrar ) = [](){\
++            TestName<INTERNAL_CATCH_MAKE_TYPE_LISTS_FROM_TYPES(__VA_ARGS__)>();\
++            return 0;\
++        }();\
+         }\
+-        CATCH_INTERNAL_UNSUPPRESS_GLOBALS_WARNINGS \
+-        template<typename TestType> \
+-        static void TestFunc()
++        }\
++        CATCH_INTERNAL_STOP_WARNINGS_SUPPRESSION \
++        INTERNAL_CATCH_DEFINE_SIG_TEST(TestFunc,INTERNAL_CATCH_REMOVE_PARENS(Signature))
+ 
+-#if defined(CATCH_CPP17_OR_GREATER)
+-#define CATCH_INTERNAL_CHECK_UNIQUE_TYPES(...) static_assert(Catch::is_unique<__VA_ARGS__>,"Duplicate type detected in declaration of template test case");
++#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__ )
+ #else
+-#define CATCH_INTERNAL_CHECK_UNIQUE_TYPES(...) static_assert(Catch::is_unique<__VA_ARGS__>::value,"Duplicate type detected in declaration of template test case");
++    #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__ ) )
+ #endif
+ 
+ #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, __VA_ARGS__ )
++    #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__ )
+ #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, __VA_ARGS__ ) )
++    #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__ ) )
+ #endif
+ 
+-    #define INTERNAL_CATCH_TEMPLATE_REGISTRY_INITIATE(TestName, Name, ...)\
+-        static int INTERNAL_CATCH_UNIQUE_NAME( globalRegistrar ) = [](){\
+-            TestName<CATCH_REC_LIST(INTERNAL_CATCH_REMOVE_PARENS, __VA_ARGS__)>(CATCH_REC_LIST_UD(INTERNAL_CATCH_TEMPLATE_UNIQUE_NAME,Name, __VA_ARGS__));\
+-            return 0;\
+-        }();
++    #define INTERNAL_CATCH_TEMPLATE_PRODUCT_TEST_CASE2(TestName, TestFuncName, Name, Tags, Signature, TmplTypes, TypesList) \
++        CATCH_INTERNAL_START_WARNINGS_SUPPRESSION                      \
++        CATCH_INTERNAL_SUPPRESS_GLOBALS_WARNINGS                      \
++        CATCH_INTERNAL_SUPPRESS_ZERO_VARIADIC_WARNINGS                \
++        CATCH_INTERNAL_SUPPRESS_UNUSED_TEMPLATE_WARNINGS              \
++        template<typename TestType> static void TestFuncName();       \
++        namespace {\
++        namespace INTERNAL_CATCH_MAKE_NAMESPACE(TestName) {                                     \
++            INTERNAL_CATCH_TYPE_GEN                                                  \
++            INTERNAL_CATCH_NTTP_GEN(INTERNAL_CATCH_REMOVE_PARENS(Signature))         \
++            template<typename... Types>                               \
++            struct TestName {                                         \
++                void reg_tests() {                                          \
++                    int index = 0;                                    \
++                    using expander = int[];                           \
++                    constexpr char const* tmpl_types[] = {CATCH_REC_LIST(INTERNAL_CATCH_STRINGIZE_WITHOUT_PARENS, INTERNAL_CATCH_REMOVE_PARENS(TmplTypes))};\
++                    constexpr char const* types_list[] = {CATCH_REC_LIST(INTERNAL_CATCH_STRINGIZE_WITHOUT_PARENS, INTERNAL_CATCH_REMOVE_PARENS(TypesList))};\
++                    constexpr auto num_types = sizeof(types_list) / sizeof(types_list[0]);\
++                    (void)expander{(Catch::AutoReg( Catch::makeTestInvoker( &TestFuncName<Types> ), CATCH_INTERNAL_LINEINFO, Catch::StringRef(), Catch::NameAndTags{ Name " - " + std::string(tmpl_types[index / num_types]) + "<" + std::string(types_list[index % num_types]) + ">", Tags } ), index++)... };/* NOLINT */\
++                }                                                     \
++            };                                                        \
++            static int INTERNAL_CATCH_UNIQUE_NAME( globalRegistrar ) = [](){ \
++                using TestInit = typename create<TestName, decltype(get_wrapper<INTERNAL_CATCH_REMOVE_PARENS(TmplTypes)>()), TypeList<INTERNAL_CATCH_MAKE_TYPE_LISTS_FROM_TYPES(INTERNAL_CATCH_REMOVE_PARENS(TypesList))>>::type; \
++                TestInit t;                                           \
++                t.reg_tests();                                        \
++                return 0;                                             \
++            }();                                                      \
++        }                                                             \
++        }                                                             \
++        CATCH_INTERNAL_STOP_WARNINGS_SUPPRESSION                       \
++        template<typename TestType>                                   \
++        static void TestFuncName()
++
++#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__)
++#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__ ) )
++#endif
+ 
+-    #define INTERNAL_CATCH_TEMPLATE_TEST_CASE_METHOD_2( TestNameClass, TestName, ClassName, Name, Tags, ... ) \
++#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__)
++#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__ ) )
++#endif
++
++    #define INTERNAL_CATCH_TEMPLATE_LIST_TEST_CASE_2(TestName, TestFunc, Name, Tags, TmplList)\
++        CATCH_INTERNAL_START_WARNINGS_SUPPRESSION \
+         CATCH_INTERNAL_SUPPRESS_GLOBALS_WARNINGS \
+-        namespace{ \
+-            template<typename TestType> \
++        CATCH_INTERNAL_SUPPRESS_UNUSED_TEMPLATE_WARNINGS \
++        template<typename TestType> static void TestFunc();       \
++        namespace {\
++        namespace INTERNAL_CATCH_MAKE_NAMESPACE(TestName){\
++        INTERNAL_CATCH_TYPE_GEN\
++        template<typename... Types>                               \
++        struct TestName {                                         \
++            void reg_tests() {                                          \
++                int index = 0;                                    \
++                using expander = int[];                           \
++                (void)expander{(Catch::AutoReg( Catch::makeTestInvoker( &TestFunc<Types> ), CATCH_INTERNAL_LINEINFO, Catch::StringRef(), Catch::NameAndTags{ Name " - " + std::string(INTERNAL_CATCH_STRINGIZE(TmplList)) + " - " + std::to_string(index), Tags } ), index++)... };/* NOLINT */\
++            }                                                     \
++        };\
++        static int INTERNAL_CATCH_UNIQUE_NAME( globalRegistrar ) = [](){ \
++                using TestInit = typename convert<TestName, TmplList>::type; \
++                TestInit t;                                           \
++                t.reg_tests();                                        \
++                return 0;                                             \
++            }();                                                      \
++        }}\
++        CATCH_INTERNAL_STOP_WARNINGS_SUPPRESSION                       \
++        template<typename TestType>                                   \
++        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 )
++
++    #define INTERNAL_CATCH_TEMPLATE_TEST_CASE_METHOD_2( TestNameClass, TestName, ClassName, Name, Tags, Signature, ... ) \
++        CATCH_INTERNAL_START_WARNINGS_SUPPRESSION \
++        CATCH_INTERNAL_SUPPRESS_GLOBALS_WARNINGS \
++        CATCH_INTERNAL_SUPPRESS_ZERO_VARIADIC_WARNINGS \
++        CATCH_INTERNAL_SUPPRESS_UNUSED_TEMPLATE_WARNINGS \
++        namespace {\
++        namespace INTERNAL_CATCH_MAKE_NAMESPACE(TestName){ \
++            INTERNAL_CATCH_TYPE_GEN\
++            INTERNAL_CATCH_NTTP_GEN(INTERNAL_CATCH_REMOVE_PARENS(Signature))\
++            INTERNAL_CATCH_DECLARE_SIG_TEST_METHOD(TestName, ClassName, INTERNAL_CATCH_REMOVE_PARENS(Signature));\
++            INTERNAL_CATCH_NTTP_REG_METHOD_GEN(TestName, INTERNAL_CATCH_REMOVE_PARENS(Signature))\
++            template<typename...Types> \
++            struct TestNameClass{\
++                TestNameClass(){\
++                    int index = 0;                                    \
++                    constexpr char const* tmpl_types[] = {CATCH_REC_LIST(INTERNAL_CATCH_STRINGIZE_WITHOUT_PARENS, __VA_ARGS__)};\
++                    using expander = int[];\
++                    (void)expander{(reg_test(Types{}, #ClassName, Catch::NameAndTags{ Name " - " + std::string(tmpl_types[index]), Tags } ), index++)... };/* NOLINT */ \
++                }\
++            };\
++            static int INTERNAL_CATCH_UNIQUE_NAME( globalRegistrar ) = [](){\
++                TestNameClass<INTERNAL_CATCH_MAKE_TYPE_LISTS_FROM_TYPES(__VA_ARGS__)>();\
++                return 0;\
++        }();\
++        }\
++        }\
++        CATCH_INTERNAL_STOP_WARNINGS_SUPPRESSION \
++        INTERNAL_CATCH_DEFINE_SIG_TEST_METHOD(TestName, INTERNAL_CATCH_REMOVE_PARENS(Signature))
++
++#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__ )
++#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__ ) )
++#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__ )
++#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__ ) )
++#endif
++
++    #define INTERNAL_CATCH_TEMPLATE_PRODUCT_TEST_CASE_METHOD_2(TestNameClass, TestName, ClassName, Name, Tags, Signature, TmplTypes, TypesList)\
++        CATCH_INTERNAL_START_WARNINGS_SUPPRESSION \
++        CATCH_INTERNAL_SUPPRESS_GLOBALS_WARNINGS \
++        CATCH_INTERNAL_SUPPRESS_ZERO_VARIADIC_WARNINGS \
++        CATCH_INTERNAL_SUPPRESS_UNUSED_TEMPLATE_WARNINGS \
++        template<typename TestType> \
+             struct TestName : INTERNAL_CATCH_REMOVE_PARENS(ClassName <TestType>) { \
+                 void test();\
+             };\
+-            template<typename...Types> \
++        namespace {\
++        namespace INTERNAL_CATCH_MAKE_NAMESPACE(TestNameClass) {\
++            INTERNAL_CATCH_TYPE_GEN                  \
++            INTERNAL_CATCH_NTTP_GEN(INTERNAL_CATCH_REMOVE_PARENS(Signature))\
++            template<typename...Types>\
+             struct TestNameClass{\
+-                template<typename...Ts> \
+-                TestNameClass(Ts...names){\
+-                    CATCH_INTERNAL_CHECK_UNIQUE_TYPES(CATCH_REC_LIST(INTERNAL_CATCH_REMOVE_PARENS, __VA_ARGS__)) \
++                void reg_tests(){\
++                    int index = 0;\
+                     using expander = int[];\
+-                    (void)expander{(Catch::AutoReg( Catch::makeTestInvoker( &TestName<Types>::test ), CATCH_INTERNAL_LINEINFO, #ClassName, Catch::NameAndTags{ names, Tags } ), 0)... };/* NOLINT */ \
++                    constexpr char const* tmpl_types[] = {CATCH_REC_LIST(INTERNAL_CATCH_STRINGIZE_WITHOUT_PARENS, INTERNAL_CATCH_REMOVE_PARENS(TmplTypes))};\
++                    constexpr char const* types_list[] = {CATCH_REC_LIST(INTERNAL_CATCH_STRINGIZE_WITHOUT_PARENS, INTERNAL_CATCH_REMOVE_PARENS(TypesList))};\
++                    constexpr auto num_types = sizeof(types_list) / sizeof(types_list[0]);\
++                    (void)expander{(Catch::AutoReg( Catch::makeTestInvoker( &TestName<Types>::test ), CATCH_INTERNAL_LINEINFO, #ClassName, Catch::NameAndTags{ Name " - " + std::string(tmpl_types[index / num_types]) + "<" + std::string(types_list[index % num_types]) + ">", Tags } ), index++)... };/* NOLINT */ \
+                 }\
+             };\
+-            INTERNAL_CATCH_TEMPLATE_REGISTRY_INITIATE(TestNameClass, Name, __VA_ARGS__)\
++            static int INTERNAL_CATCH_UNIQUE_NAME( globalRegistrar ) = [](){\
++                using TestInit = typename create<TestNameClass, decltype(get_wrapper<INTERNAL_CATCH_REMOVE_PARENS(TmplTypes)>()), TypeList<INTERNAL_CATCH_MAKE_TYPE_LISTS_FROM_TYPES(INTERNAL_CATCH_REMOVE_PARENS(TypesList))>>::type;\
++                TestInit t;\
++                t.reg_tests();\
++                return 0;\
++            }(); \
++        }\
+         }\
+-        CATCH_INTERNAL_UNSUPPRESS_GLOBALS_WARNINGS\
++        CATCH_INTERNAL_STOP_WARNINGS_SUPPRESSION \
+         template<typename TestType> \
+         void TestName<TestType>::test()
+ 
+ #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, __VA_ARGS__ )
++    #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__ )
+ #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, __VA_ARGS__ ) )
++    #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__ ) )
+ #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__ )
++#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__ ) )
++#endif
++
++    #define INTERNAL_CATCH_TEMPLATE_LIST_TEST_CASE_METHOD_2( TestNameClass, TestName, ClassName, Name, Tags, TmplList) \
++        CATCH_INTERNAL_START_WARNINGS_SUPPRESSION \
++        CATCH_INTERNAL_SUPPRESS_GLOBALS_WARNINGS \
++        CATCH_INTERNAL_SUPPRESS_UNUSED_TEMPLATE_WARNINGS \
++        template<typename TestType> \
++        struct TestName : INTERNAL_CATCH_REMOVE_PARENS(ClassName <TestType>) { \
++            void test();\
++        };\
++        namespace {\
++        namespace INTERNAL_CATCH_MAKE_NAMESPACE(TestName){ \
++            INTERNAL_CATCH_TYPE_GEN\
++            template<typename...Types>\
++            struct TestNameClass{\
++                void reg_tests(){\
++                    int index = 0;\
++                    using expander = int[];\
++                    (void)expander{(Catch::AutoReg( Catch::makeTestInvoker( &TestName<Types>::test ), CATCH_INTERNAL_LINEINFO, #ClassName, Catch::NameAndTags{ Name " - " + std::string(INTERNAL_CATCH_STRINGIZE(TmplList)) + " - " + std::to_string(index), Tags } ), index++)... };/* NOLINT */ \
++                }\
++            };\
++            static int INTERNAL_CATCH_UNIQUE_NAME( globalRegistrar ) = [](){\
++                using TestInit = typename convert<TestNameClass, TmplList>::type;\
++                TestInit t;\
++                t.reg_tests();\
++                return 0;\
++            }(); \
++        }}\
++        CATCH_INTERNAL_STOP_WARNINGS_SUPPRESSION \
++        template<typename TestType> \
++        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 )
++
+ // end catch_test_registry.h
+ // start catch_capture.hpp
+ 
+@@ -978,7 +1437,7 @@
+ 
+     auto makeStream( StringRef const &filename ) -> IStream const*;
+ 
+-    class ReusableStringStream {
++    class ReusableStringStream : NonCopyable {
+         std::size_t m_index;
+         std::ostream* m_oss;
+     public:
+@@ -997,6 +1456,42 @@
+ }
+ 
+ // end catch_stream.h
++// start catch_interfaces_enum_values_registry.h
++
++#include <vector>
++
++namespace Catch {
++
++    namespace Detail {
++        struct EnumInfo {
++            StringRef m_name;
++            std::vector<std::pair<int, StringRef>> m_values;
++
++            ~EnumInfo();
++
++            StringRef lookup( int value ) const;
++        };
++    } // namespace Detail
++
++    struct IMutableEnumValuesRegistry {
++        virtual ~IMutableEnumValuesRegistry();
++
++        virtual Detail::EnumInfo const& registerEnum( StringRef enumName, StringRef allEnums, std::vector<int> const& values ) = 0;
++
++        template<typename E>
++        Detail::EnumInfo const& registerEnum( StringRef enumName, StringRef allEnums, std::initializer_list<E> values ) {
++            static_assert(sizeof(int) >= sizeof(E), "Cannot serialize enum to int");
++            std::vector<int> intValues;
++            intValues.reserve( values.size() );
++            for( auto enumValue : values )
++                intValues.push_back( static_cast<int>( enumValue ) );
++            return registerEnum( enumName, allEnums, intValues );
++        }
++    };
++
++} // Catch
++
++// end catch_interfaces_enum_values_registry.h
+ 
+ #ifdef CATCH_CONFIG_CPP17_STRING_VIEW
+ #include <string_view>
+@@ -1067,9 +1562,9 @@
+ 
+         template<typename T>
+         class IsStreamInsertable {
+-            template<typename SS, typename TT>
++            template<typename Stream, typename U>
+             static auto test(int)
+-                -> decltype(std::declval<SS&>() << std::declval<TT>(), std::true_type());
++                -> decltype(std::declval<Stream&>() << std::declval<U>(), std::true_type());
+ 
+             template<typename, typename>
+             static auto test(...)->std::false_type;
+@@ -1231,6 +1726,12 @@
+         }
+     };
+ 
++#if defined(CATCH_CONFIG_CPP17_BYTE)
++    template<>
++    struct StringMaker<std::byte> {
++        static std::string convert(std::byte value);
++    };
++#endif // defined(CATCH_CONFIG_CPP17_BYTE)
+     template<>
+     struct StringMaker<int> {
+         static std::string convert(int value);
+@@ -1282,10 +1783,13 @@
+     template<>
+     struct StringMaker<float> {
+         static std::string convert(float value);
++        static int precision;
+     };
++
+     template<>
+     struct StringMaker<double> {
+         static std::string convert(double value);
++        static int precision;
+     };
+ 
+     template <typename T>
+@@ -1321,8 +1825,8 @@
+ #endif
+ 
+     namespace Detail {
+-        template<typename InputIterator>
+-        std::string rangeToString(InputIterator first, InputIterator last) {
++        template<typename InputIterator, typename Sentinel = InputIterator>
++        std::string rangeToString(InputIterator first, Sentinel last) {
+             ReusableStringStream rss;
+             rss << "{ ";
+             if (first != last) {
+@@ -1370,6 +1874,7 @@
+ #  define CATCH_CONFIG_ENABLE_TUPLE_STRINGMAKER
+ #  define CATCH_CONFIG_ENABLE_VARIANT_STRINGMAKER
+ #  define CATCH_CONFIG_ENABLE_CHRONO_STRINGMAKER
++#  define CATCH_CONFIG_ENABLE_OPTIONAL_STRINGMAKER
+ #endif
+ 
+ // Separate std::pair specialization
+@@ -1391,6 +1896,24 @@
+ }
+ #endif // CATCH_CONFIG_ENABLE_PAIR_STRINGMAKER
+ 
++#if defined(CATCH_CONFIG_ENABLE_OPTIONAL_STRINGMAKER) && defined(CATCH_CONFIG_CPP17_OPTIONAL)
++#include <optional>
++namespace Catch {
++    template<typename T>
++    struct StringMaker<std::optional<T> > {
++        static std::string convert(const std::optional<T>& optional) {
++            ReusableStringStream rss;
++            if (optional.has_value()) {
++                rss << ::Catch::Detail::stringify(*optional);
++            } else {
++                rss << "{ }";
++            }
++            return rss.str();
++        }
++    };
++}
++#endif // CATCH_CONFIG_ENABLE_OPTIONAL_STRINGMAKER
++
+ // Separate std::tuple specialization
+ #if defined(CATCH_CONFIG_ENABLE_TUPLE_STRINGMAKER)
+ #include <tuple>
+@@ -1461,20 +1984,27 @@
+ #endif // CATCH_CONFIG_ENABLE_VARIANT_STRINGMAKER
+ 
+ namespace Catch {
+-    struct not_this_one {}; // Tag type for detecting which begin/ end are being selected
+-
+-    // Import begin/ end from std here so they are considered alongside the fallback (...) overloads in this namespace
++    // Import begin/ end from std here
+     using std::begin;
+     using std::end;
+ 
+-    not_this_one begin( ... );
+-    not_this_one end( ... );
++    namespace detail {
++        template <typename...>
++        struct void_type {
++            using type = void;
++        };
++
++        template <typename T, typename = void>
++        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
+ 
+     template <typename T>
+-    struct is_range {
+-        static const bool value =
+-            !std::is_same<decltype(begin(std::declval<T>())), not_this_one>::value &&
+-            !std::is_same<decltype(end(std::declval<T>())), not_this_one>::value;
++    struct is_range : detail::is_range_impl<T> {
+     };
+ 
+ #if defined(_MANAGED) // Managed types are never ranges
+@@ -1639,6 +2169,18 @@
+ }
+ #endif // CATCH_CONFIG_ENABLE_CHRONO_STRINGMAKER
+ 
++#define INTERNAL_CATCH_REGISTER_ENUM( enumName, ... ) \
++namespace Catch { \
++    template<> struct StringMaker<enumName> { \
++        static std::string convert( enumName value ) { \
++            static const auto& enumInfo = ::Catch::getMutableRegistryHub().getMutableEnumValuesRegistry().registerEnum( #enumName, #__VA_ARGS__, { __VA_ARGS__ } ); \
++            return static_cast<std::string>(enumInfo.lookup( static_cast<int>( value ) )); \
++        } \
++    }; \
++}
++
++#define CATCH_REGISTER_ENUM( enumName, ... ) INTERNAL_CATCH_REGISTER_ENUM( enumName, __VA_ARGS__ )
++
+ #ifdef _MSC_VER
+ #pragma warning(pop)
+ #endif
+@@ -1652,6 +2194,7 @@
+ #pragma warning(disable:4018) // more "signed/unsigned mismatch"
+ #pragma warning(disable:4312) // Converting int to T* using reinterpret_cast (issue on x64 platform)
+ #pragma warning(disable:4180) // qualifier applied to function type has no meaning
++#pragma warning(disable:4800) // Forcing result to true or false
+ #endif
+ 
+ namespace Catch {
+@@ -1695,6 +2238,62 @@
+             m_op( op ),
+             m_rhs( rhs )
+         {}
++
++        template<typename T>
++        auto operator && ( T ) const -> BinaryExpr<LhsT, RhsT const&> const {
++            static_assert(always_false<T>::value,
++            "chained comparisons are not supported inside assertions, "
++            "wrap the expression inside parentheses, or decompose it");
++        }
++
++        template<typename T>
++        auto operator || ( T ) const -> BinaryExpr<LhsT, RhsT const&> const {
++            static_assert(always_false<T>::value,
++            "chained comparisons are not supported inside assertions, "
++            "wrap the expression inside parentheses, or decompose it");
++        }
++
++        template<typename T>
++        auto operator == ( T ) const -> BinaryExpr<LhsT, RhsT const&> const {
++            static_assert(always_false<T>::value,
++            "chained comparisons are not supported inside assertions, "
++            "wrap the expression inside parentheses, or decompose it");
++        }
++
++        template<typename T>
++        auto operator != ( T ) const -> BinaryExpr<LhsT, RhsT const&> const {
++            static_assert(always_false<T>::value,
++            "chained comparisons are not supported inside assertions, "
++            "wrap the expression inside parentheses, or decompose it");
++        }
++
++        template<typename T>
++        auto operator > ( T ) const -> BinaryExpr<LhsT, RhsT const&> const {
++            static_assert(always_false<T>::value,
++            "chained comparisons are not supported inside assertions, "
++            "wrap the expression inside parentheses, or decompose it");
++        }
++
++        template<typename T>
++        auto operator < ( T ) const -> BinaryExpr<LhsT, RhsT const&> const {
++            static_assert(always_false<T>::value,
++            "chained comparisons are not supported inside assertions, "
++            "wrap the expression inside parentheses, or decompose it");
++        }
++
++        template<typename T>
++        auto operator >= ( T ) const -> BinaryExpr<LhsT, RhsT const&> const {
++            static_assert(always_false<T>::value,
++            "chained comparisons are not supported inside assertions, "
++            "wrap the expression inside parentheses, or decompose it");
++        }
++
++        template<typename T>
++        auto operator <= ( T ) const -> BinaryExpr<LhsT, RhsT const&> const {
++            static_assert(always_false<T>::value,
++            "chained comparisons are not supported inside assertions, "
++            "wrap the expression inside parentheses, or decompose it");
++        }
+     };
+ 
+     template<typename LhsT>
+@@ -1707,7 +2306,7 @@
+ 
+     public:
+         explicit UnaryExpr( LhsT lhs )
+-        :   ITransientExpression{ false, lhs ? true : false },
++        :   ITransientExpression{ false, static_cast<bool>(lhs) },
+             m_lhs( lhs )
+         {}
+     };
+@@ -1773,6 +2372,32 @@
+         auto operator <= ( RhsT const& rhs ) -> BinaryExpr<LhsT, RhsT const&> const {
+             return { static_cast<bool>(m_lhs <= rhs), m_lhs, "<=", rhs };
+         }
++        template <typename RhsT>
++        auto operator | (RhsT const& rhs) -> BinaryExpr<LhsT, RhsT const&> const {
++            return { static_cast<bool>(m_lhs | rhs), m_lhs, "|", rhs };
++        }
++        template <typename RhsT>
++        auto operator & (RhsT const& rhs) -> BinaryExpr<LhsT, RhsT const&> const {
++            return { static_cast<bool>(m_lhs & rhs), m_lhs, "&", rhs };
++        }
++        template <typename RhsT>
++        auto operator ^ (RhsT const& rhs) -> BinaryExpr<LhsT, RhsT const&> const {
++            return { static_cast<bool>(m_lhs ^ rhs), m_lhs, "^", rhs };
++        }
++
++        template<typename RhsT>
++        auto operator && ( RhsT const& ) -> BinaryExpr<LhsT, RhsT const&> 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 {
++            static_assert(always_false<RhsT>::value,
++            "operator|| is not supported inside assertions, "
++            "wrap the expression inside parentheses, or decompose it");
++        }
+ 
+         auto makeUnaryExpr() const -> UnaryExpr<LhsT> {
+             return UnaryExpr<LhsT>{ m_lhs };
+@@ -1807,6 +2432,7 @@
+ // start catch_interfaces_capture.h
+ 
+ #include <string>
++#include <chrono>
+ 
+ namespace Catch {
+ 
+@@ -1815,15 +2441,20 @@
+     struct SectionInfo;
+     struct SectionEndInfo;
+     struct MessageInfo;
++    struct MessageBuilder;
+     struct Counts;
+-    struct BenchmarkInfo;
+-    struct BenchmarkStats;
+     struct AssertionReaction;
+     struct SourceLineInfo;
+ 
+     struct ITransientExpression;
+     struct IGeneratorTracker;
+ 
++#if defined(CATCH_CONFIG_ENABLE_BENCHMARKING)
++    struct BenchmarkInfo;
++    template <typename Duration = std::chrono::duration<double, std::nano>>
++    struct BenchmarkStats;
++#endif // CATCH_CONFIG_ENABLE_BENCHMARKING
++
+     struct IResultCapture {
+ 
+         virtual ~IResultCapture();
+@@ -1833,14 +2464,20 @@
+         virtual void sectionEnded( SectionEndInfo const& endInfo ) = 0;
+         virtual void sectionEndedEarly( SectionEndInfo const& endInfo ) = 0;
+ 
+-        virtual auto acquireGeneratorTracker( SourceLineInfo const& lineInfo ) -> IGeneratorTracker& = 0;
++        virtual auto acquireGeneratorTracker( StringRef generatorName, SourceLineInfo const& lineInfo ) -> IGeneratorTracker& = 0;
+ 
++#if defined(CATCH_CONFIG_ENABLE_BENCHMARKING)
++        virtual void benchmarkPreparing( std::string const& name ) = 0;
+         virtual void benchmarkStarting( BenchmarkInfo const& info ) = 0;
+-        virtual void benchmarkEnded( BenchmarkStats const& stats ) = 0;
++        virtual void benchmarkEnded( BenchmarkStats<> const& stats ) = 0;
++        virtual void benchmarkFailed( std::string const& error ) = 0;
++#endif // CATCH_CONFIG_ENABLE_BENCHMARKING
+ 
+         virtual void pushScopedMessage( MessageInfo const& message ) = 0;
+         virtual void popScopedMessage( MessageInfo const& message ) = 0;
+ 
++        virtual void emplaceUnscopedMessage( MessageBuilder const& builder ) = 0;
++
+         virtual void handleFatalErrorCondition( StringRef message ) = 0;
+ 
+         virtual void handleExpr
+@@ -2004,9 +2641,12 @@
+     class ScopedMessage {
+     public:
+         explicit ScopedMessage( MessageBuilder const& builder );
++        ScopedMessage( ScopedMessage& duplicate ) = delete;
++        ScopedMessage( ScopedMessage&& old );
+         ~ScopedMessage();
+ 
+         MessageInfo m_info;
++        bool m_moved;
+     };
+ 
+     class Capturer {
+@@ -2062,15 +2702,16 @@
+ ///////////////////////////////////////////////////////////////////////////////
+ #define INTERNAL_CATCH_TEST( macroName, resultDisposition, ... ) \
+     do { \
++        CATCH_INTERNAL_IGNORE_BUT_WARN(__VA_ARGS__); \
+         Catch::AssertionHandler catchAssertionHandler( macroName##_catch_sr, CATCH_INTERNAL_LINEINFO, CATCH_INTERNAL_STRINGIFY(__VA_ARGS__), resultDisposition ); \
+         INTERNAL_CATCH_TRY { \
++            CATCH_INTERNAL_START_WARNINGS_SUPPRESSION \
+             CATCH_INTERNAL_SUPPRESS_PARENTHESES_WARNINGS \
+             catchAssertionHandler.handleExpr( Catch::Decomposer() <= __VA_ARGS__ ); \
+-            CATCH_INTERNAL_UNSUPPRESS_PARENTHESES_WARNINGS \
++            CATCH_INTERNAL_STOP_WARNINGS_SUPPRESSION \
+         } INTERNAL_CATCH_CATCH( catchAssertionHandler ) \
+         INTERNAL_CATCH_REACT( catchAssertionHandler ) \
+-    } while( (void)0, false && static_cast<bool>( !!(__VA_ARGS__) ) ) // the expression here is never evaluated at runtime but it forces the compiler to give it a look
+-    // The double negation silences MSVC's C4800 warning, the static_cast forces short-circuit evaluation if the type has overloaded &&.
++    } while( (void)0, (false) && static_cast<bool>( !!(__VA_ARGS__) ) )
+ 
+ ///////////////////////////////////////////////////////////////////////////////
+ #define INTERNAL_CATCH_IF( macroName, resultDisposition, ... ) \
+@@ -2151,6 +2792,10 @@
+     Catch::ScopedMessage INTERNAL_CATCH_UNIQUE_NAME( scopedMessage )( Catch::MessageBuilder( macroName##_catch_sr, CATCH_INTERNAL_LINEINFO, Catch::ResultWas::Info ) << log );
+ 
+ ///////////////////////////////////////////////////////////////////////////////
++#define INTERNAL_CATCH_UNSCOPED_INFO( macroName, log ) \
++    Catch::getResultCapture().emplaceUnscopedMessage( Catch::MessageBuilder( macroName##_catch_sr, CATCH_INTERNAL_LINEINFO, Catch::ResultWas::Info ) << log )
++
++///////////////////////////////////////////////////////////////////////////////
+ // Although this is matcher-based, it can be used with just a string
+ #define INTERNAL_CATCH_THROWS_STR_MATCHES( macroName, resultDisposition, matcher, ... ) \
+     do { \
+@@ -2283,62 +2928,18 @@
+ } // end namespace Catch
+ 
+ #define INTERNAL_CATCH_SECTION( ... ) \
++    CATCH_INTERNAL_START_WARNINGS_SUPPRESSION \
+     CATCH_INTERNAL_SUPPRESS_UNUSED_WARNINGS \
+     if( Catch::Section const& INTERNAL_CATCH_UNIQUE_NAME( catch_internal_Section ) = Catch::SectionInfo( CATCH_INTERNAL_LINEINFO, __VA_ARGS__ ) ) \
+-    CATCH_INTERNAL_UNSUPPRESS_UNUSED_WARNINGS
++    CATCH_INTERNAL_STOP_WARNINGS_SUPPRESSION
+ 
+ #define INTERNAL_CATCH_DYNAMIC_SECTION( ... ) \
++    CATCH_INTERNAL_START_WARNINGS_SUPPRESSION \
+     CATCH_INTERNAL_SUPPRESS_UNUSED_WARNINGS \
+     if( Catch::Section const& INTERNAL_CATCH_UNIQUE_NAME( catch_internal_Section ) = Catch::SectionInfo( CATCH_INTERNAL_LINEINFO, (Catch::ReusableStringStream() << __VA_ARGS__).str() ) ) \
+-    CATCH_INTERNAL_UNSUPPRESS_UNUSED_WARNINGS
++    CATCH_INTERNAL_STOP_WARNINGS_SUPPRESSION
+ 
+ // end catch_section.h
+-// start catch_benchmark.h
+-
+-#include <cstdint>
+-#include <string>
+-
+-namespace Catch {
+-
+-    class BenchmarkLooper {
+-
+-        std::string m_name;
+-        std::size_t m_count = 0;
+-        std::size_t m_iterationsToRun = 1;
+-        uint64_t m_resolution;
+-        Timer m_timer;
+-
+-        static auto getResolution() -> uint64_t;
+-    public:
+-        // Keep most of this inline as it's on the code path that is being timed
+-        BenchmarkLooper( StringRef name )
+-        :   m_name( name ),
+-            m_resolution( getResolution() )
+-        {
+-            reportStart();
+-            m_timer.start();
+-        }
+-
+-        explicit operator bool() {
+-            if( m_count < m_iterationsToRun )
+-                return true;
+-            return needsMoreIterations();
+-        }
+-
+-        void increment() {
+-            ++m_count;
+-        }
+-
+-        void reportStart();
+-        auto needsMoreIterations() -> bool;
+-    };
+-
+-} // end namespace Catch
+-
+-#define BENCHMARK( name ) \
+-    for( Catch::BenchmarkLooper looper( name ); looper; looper.increment() )
+-
+-// end catch_benchmark.h
+ // start catch_interfaces_exception.h
+ 
+ // start catch_interfaces_registry_hub.h
+@@ -2355,6 +2956,8 @@
+     struct IReporterRegistry;
+     struct IReporterFactory;
+     struct ITagAliasRegistry;
++    struct IMutableEnumValuesRegistry;
++
+     class StartupExceptionRegistry;
+ 
+     using IReporterFactoryPtr = std::shared_ptr<IReporterFactory>;
+@@ -2365,7 +2968,6 @@
+         virtual IReporterRegistry const& getReporterRegistry() const = 0;
+         virtual ITestCaseRegistry const& getTestCaseRegistry() const = 0;
+         virtual ITagAliasRegistry const& getTagAliasRegistry() const = 0;
+-
+         virtual IExceptionTranslatorRegistry const& getExceptionTranslatorRegistry() const = 0;
+ 
+         virtual StartupExceptionRegistry const& getStartupExceptionRegistry() const = 0;
+@@ -2379,6 +2981,7 @@
+         virtual void registerTranslator( const 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;
+     };
+ 
+     IRegistryHub const& getRegistryHub();
+@@ -2425,6 +3028,9 @@
+             {}
+ 
+             std::string translate( ExceptionTranslators::const_iterator it, ExceptionTranslators::const_iterator itEnd ) const override {
++#if defined(CATCH_CONFIG_DISABLE_EXCEPTIONS)
++                return "";
++#else
+                 try {
+                     if( it == itEnd )
+                         std::rethrow_exception(std::current_exception());
+@@ -2434,6 +3040,7 @@
+                 catch( T& ex ) {
+                     return m_translateFunction( ex );
+                 }
++#endif
+             }
+ 
+         protected:
+@@ -2452,9 +3059,10 @@
+ ///////////////////////////////////////////////////////////////////////////////
+ #define INTERNAL_CATCH_TRANSLATE_EXCEPTION2( translatorName, signature ) \
+     static std::string translatorName( signature ); \
++    CATCH_INTERNAL_START_WARNINGS_SUPPRESSION \
+     CATCH_INTERNAL_SUPPRESS_GLOBALS_WARNINGS \
+     namespace{ Catch::ExceptionTranslatorRegistrar INTERNAL_CATCH_UNIQUE_NAME( catch_internal_ExceptionRegistrar )( &translatorName ); } \
+-    CATCH_INTERNAL_UNSUPPRESS_GLOBALS_WARNINGS \
++    CATCH_INTERNAL_STOP_WARNINGS_SUPPRESSION \
+     static std::string translatorName( signature )
+ 
+ #define INTERNAL_CATCH_TRANSLATE_EXCEPTION( signature ) INTERNAL_CATCH_TRANSLATE_EXCEPTION2( INTERNAL_CATCH_UNIQUE_NAME( catch_internal_ExceptionTranslator ), signature )
+@@ -2485,7 +3093,7 @@
+         Approx operator-() const;
+ 
+         template <typename T, typename = typename std::enable_if<std::is_constructible<double, T>::value>::type>
+-        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;
+@@ -2585,6 +3193,7 @@
+ 
+ #include <string>
+ #include <iosfwd>
++#include <vector>
+ 
+ namespace Catch {
+ 
+@@ -2595,7 +3204,13 @@
+     bool contains( std::string const& s, std::string const& infix );
+     void toLowerInPlace( std::string& s );
+     std::string toLower( std::string const& s );
++    //! Returns a new string without whitespace at the start/end
+     std::string trim( std::string const& str );
++    //! Returns a substring of the original ref without whitespace. Beware lifetimes!
++    StringRef trim(StringRef ref);
++
++    // !!! Be aware, returns refs into original string - make sure original string outlives them
++    std::vector<StringRef> splitStringRef( StringRef str, char delimiter );
+     bool replaceInPlace( std::string& str, std::string const& replaceThis, std::string const& withThis );
+ 
+     struct pluralise {
+@@ -2648,6 +3263,15 @@
+             virtual bool match( ObjectT const& arg ) const = 0;
+         };
+ 
++#if defined(__OBJC__)
++        // Hack to fix Catch GH issue #1661. Could use id for generic Object support.
++        // use of const for Object pointers is very uncommon and under ARC it causes some kind of signature mismatch that breaks compilation
++        template<>
++        struct MatcherMethod<NSString*> {
++            virtual bool match( NSString* arg ) const = 0;
++        };
++#endif
++
+ #ifdef __clang__
+ #    pragma clang diagnostic pop
+ #endif
+@@ -2685,9 +3309,10 @@
+                 return description;
+             }
+ 
+-            MatchAllOf<ArgT>& operator && ( MatcherBase<ArgT> const& other ) {
+-                m_matchers.push_back( &other );
+-                return *this;
++            MatchAllOf<ArgT> operator && ( MatcherBase<ArgT> const& other ) {
++                auto copy(*this);
++                copy.m_matchers.push_back( &other );
++                return copy;
+             }
+ 
+             std::vector<MatcherBase<ArgT> const*> m_matchers;
+@@ -2718,9 +3343,10 @@
+                 return description;
+             }
+ 
+-            MatchAnyOf<ArgT>& operator || ( MatcherBase<ArgT> const& other ) {
+-                m_matchers.push_back( &other );
+-                return *this;
++            MatchAnyOf<ArgT> operator || ( MatcherBase<ArgT> const& other ) {
++                auto copy(*this);
++                copy.m_matchers.push_back( &other );
++                return copy;
+             }
+ 
+             std::vector<MatcherBase<ArgT> const*> m_matchers;
+@@ -2764,10 +3390,34 @@
+ } // namespace Catch
+ 
+ // end catch_matchers.h
+-// start catch_matchers_floating.h
++// start catch_matchers_exception.hpp
+ 
+-#include <type_traits>
+-#include <cmath>
++namespace Catch {
++namespace Matchers {
++namespace Exception {
++
++class ExceptionMessageMatcher : public MatcherBase<std::exception> {
++    std::string m_message;
++public:
++
++    ExceptionMessageMatcher(std::string const& message):
++        m_message(message)
++    {}
++
++    bool match(std::exception const& ex) const override;
++
++    std::string describe() const override;
++};
++
++} // namespace Exception
++
++Exception::ExceptionMessageMatcher Message(std::string const& message);
++
++} // namespace Matchers
++} // namespace Catch
++
++// end catch_matchers_exception.hpp
++// start catch_matchers_floating.h
+ 
+ namespace Catch {
+ namespace Matchers {
+@@ -2786,22 +3436,43 @@
+         };
+ 
+         struct WithinUlpsMatcher : MatcherBase<double> {
+-            WithinUlpsMatcher(double target, int ulps, FloatingPointKind baseType);
++            WithinUlpsMatcher(double target, uint64_t ulps, FloatingPointKind baseType);
+             bool match(double const& matchee) const override;
+             std::string describe() const override;
+         private:
+             double m_target;
+-            int m_ulps;
++            uint64_t m_ulps;
+             FloatingPointKind m_type;
+         };
+ 
++        // Given IEEE-754 format for floats and doubles, we can assume
++        // that float -> double promotion is lossless. Given this, we can
++        // assume that if we do the standard relative comparison of
++        // |lhs - rhs| <= epsilon * max(fabs(lhs), fabs(rhs)), then we get
++        // the same result if we do this for floats, as if we do this for
++        // doubles that were promoted from floats.
++        struct WithinRelMatcher : MatcherBase<double> {
++            WithinRelMatcher(double target, double epsilon);
++            bool match(double const& matchee) const override;
++            std::string describe() const override;
++        private:
++            double m_target;
++            double m_epsilon;
++        };
++
+     } // namespace Floating
+ 
+     // The following functions create the actual matcher objects.
+     // This allows the types to be inferred
+-    Floating::WithinUlpsMatcher WithinULP(double target, int maxUlpDiff);
+-    Floating::WithinUlpsMatcher WithinULP(float target, int maxUlpDiff);
++    Floating::WithinUlpsMatcher WithinULP(double target, uint64_t maxUlpDiff);
++    Floating::WithinUlpsMatcher WithinULP(float target, uint64_t maxUlpDiff);
+     Floating::WithinAbsMatcher WithinAbs(double target, double margin);
++    Floating::WithinRelMatcher WithinRel(double target, double eps);
++    // defaults epsilon to 100*numeric_limits<double>::epsilon()
++    Floating::WithinRelMatcher WithinRel(double target);
++    Floating::WithinRelMatcher WithinRel(float target, float eps);
++    // defaults epsilon to 100*numeric_limits<float>::epsilon()
++    Floating::WithinRelMatcher WithinRel(float target);
+ 
+ } // namespace Matchers
+ } // namespace Catch
+@@ -2844,7 +3515,7 @@
+ 
+     // The following functions create the actual matcher objects.
+     // The user has to explicitly specify type to the function, because
+-    // infering std::function<bool(T const&)> is hard (but possible) and
++    // inferring std::function<bool(T const&)> is hard (but possible) and
+     // requires a lot of TMP.
+     template<typename T>
+     Generic::PredicateMatcher<T> Predicate(std::function<bool(T const&)> const& predicate, std::string const& description = "") {
+@@ -2932,34 +3603,12 @@
+ namespace Matchers {
+ 
+     namespace Vector {
+-        namespace Detail {
+-            template <typename InputIterator, typename T>
+-            size_t count(InputIterator first, InputIterator last, T const& item) {
+-                size_t cnt = 0;
+-                for (; first != last; ++first) {
+-                    if (*first == item) {
+-                        ++cnt;
+-                    }
+-                }
+-                return cnt;
+-            }
+-            template <typename InputIterator, typename T>
+-            bool contains(InputIterator first, InputIterator last, T const& item) {
+-                for (; first != last; ++first) {
+-                    if (*first == item) {
+-                        return true;
+-                    }
+-                }
+-                return false;
+-            }
+-        }
+-
+-        template<typename T>
+-        struct ContainsElementMatcher : MatcherBase<std::vector<T>> {
++        template<typename T, typename Alloc>
++        struct ContainsElementMatcher : MatcherBase<std::vector<T, Alloc>> {
+ 
+             ContainsElementMatcher(T const &comparator) : m_comparator( comparator) {}
+ 
+-            bool match(std::vector<T> const &v) const override {
++            bool match(std::vector<T, Alloc> const &v) const override {
+                 for (auto const& el : v) {
+                     if (el == m_comparator) {
+                         return true;
+@@ -2975,12 +3624,12 @@
+             T const& m_comparator;
+         };
+ 
+-        template<typename T>
+-        struct ContainsMatcher : MatcherBase<std::vector<T>> {
++        template<typename T, typename AllocComp, typename AllocMatch>
++        struct ContainsMatcher : MatcherBase<std::vector<T, AllocMatch>> {
+ 
+-            ContainsMatcher(std::vector<T> const &comparator) : m_comparator( comparator ) {}
++            ContainsMatcher(std::vector<T, AllocComp> const &comparator) : m_comparator( comparator ) {}
+ 
+-            bool match(std::vector<T> const &v) const override {
++            bool match(std::vector<T, AllocMatch> const &v) const override {
+                 // !TBD: see note in EqualsMatcher
+                 if (m_comparator.size() > v.size())
+                     return false;
+@@ -3002,18 +3651,18 @@
+                 return "Contains: " + ::Catch::Detail::stringify( m_comparator );
+             }
+ 
+-            std::vector<T> const& m_comparator;
++            std::vector<T, AllocComp> const& m_comparator;
+         };
+ 
+-        template<typename T>
+-        struct EqualsMatcher : MatcherBase<std::vector<T>> {
++        template<typename T, typename AllocComp, typename AllocMatch>
++        struct EqualsMatcher : MatcherBase<std::vector<T, AllocMatch>> {
+ 
+-            EqualsMatcher(std::vector<T> const &comparator) : m_comparator( comparator ) {}
++            EqualsMatcher(std::vector<T, AllocComp> const &comparator) : m_comparator( comparator ) {}
+ 
+-            bool match(std::vector<T> const &v) const override {
++            bool match(std::vector<T, AllocMatch> const &v) const override {
+                 // !TBD: This currently works if all elements can be compared using !=
+                 // - a more general approach would be via a compare template that defaults
+-                // to using !=. but could be specialised for, e.g. std::vector<T> etc
++                // to using !=. but could be specialised for, e.g. std::vector<T, Alloc> etc
+                 // - then just call that directly
+                 if (m_comparator.size() != v.size())
+                     return false;
+@@ -3025,47 +3674,60 @@
+             std::string describe() const override {
+                 return "Equals: " + ::Catch::Detail::stringify( m_comparator );
+             }
+-            std::vector<T> const& m_comparator;
++            std::vector<T, AllocComp> const& m_comparator;
+         };
+ 
+-        template<typename T>
+-        struct UnorderedEqualsMatcher : MatcherBase<std::vector<T>> {
+-            UnorderedEqualsMatcher(std::vector<T> const& target) : m_target(target) {}
+-            bool match(std::vector<T> const& vec) const override {
+-                // Note: This is a reimplementation of std::is_permutation,
+-                //       because I don't want to include <algorithm> inside the common path
+-                if (m_target.size() != vec.size()) {
+-                    return false;
+-                }
+-                auto lfirst = m_target.begin(), llast = m_target.end();
+-                auto rfirst = vec.begin(), rlast = vec.end();
+-                // Cut common prefix to optimize checking of permuted parts
+-                while (lfirst != llast && *lfirst == *rfirst) {
+-                    ++lfirst; ++rfirst;
+-                }
+-                if (lfirst == llast) {
+-                    return true;
+-                }
++        template<typename T, typename AllocComp, typename AllocMatch>
++        struct ApproxMatcher : MatcherBase<std::vector<T, AllocMatch>> {
+ 
+-                for (auto mid = lfirst; mid != llast; ++mid) {
+-                    // Skip already counted items
+-                    if (Detail::contains(lfirst, mid, *mid)) {
+-                        continue;
+-                    }
+-                    size_t num_vec = Detail::count(rfirst, rlast, *mid);
+-                    if (num_vec == 0 || Detail::count(lfirst, llast, *mid) != num_vec) {
+-                        return false;
+-                    }
+-                }
++            ApproxMatcher(std::vector<T, AllocComp> const& comparator) : m_comparator( comparator ) {}
+ 
++            bool match(std::vector<T, AllocMatch> const &v) const override {
++                if (m_comparator.size() != v.size())
++                    return false;
++                for (std::size_t i = 0; i < v.size(); ++i)
++                    if (m_comparator[i] != approx(v[i]))
++                        return false;
+                 return true;
+             }
++            std::string describe() const override {
++                return "is approx: " + ::Catch::Detail::stringify( m_comparator );
++            }
++            template <typename = typename std::enable_if<std::is_constructible<double, T>::value>::type>
++            ApproxMatcher& epsilon( T const& newEpsilon ) {
++                approx.epsilon(newEpsilon);
++                return *this;
++            }
++            template <typename = typename std::enable_if<std::is_constructible<double, T>::value>::type>
++            ApproxMatcher& margin( T const& newMargin ) {
++                approx.margin(newMargin);
++                return *this;
++            }
++            template <typename = typename std::enable_if<std::is_constructible<double, T>::value>::type>
++            ApproxMatcher& scale( T const& newScale ) {
++                approx.scale(newScale);
++                return *this;
++            }
++
++            std::vector<T, AllocComp> const& m_comparator;
++            mutable Catch::Detail::Approx approx = Catch::Detail::Approx::custom();
++        };
++
++        template<typename T, typename AllocComp, typename AllocMatch>
++        struct UnorderedEqualsMatcher : MatcherBase<std::vector<T, AllocMatch>> {
++            UnorderedEqualsMatcher(std::vector<T, AllocComp> const& target) : m_target(target) {}
++            bool match(std::vector<T, AllocMatch> const& vec) const override {
++                if (m_target.size() != vec.size()) {
++                    return false;
++                }
++                return std::is_permutation(m_target.begin(), m_target.end(), vec.begin());
++            }
+ 
+             std::string describe() const override {
+                 return "UnorderedEquals: " + ::Catch::Detail::stringify(m_target);
+             }
+         private:
+-            std::vector<T> const& m_target;
++            std::vector<T, AllocComp> const& m_target;
+         };
+ 
+     } // namespace Vector
+@@ -3073,24 +3735,29 @@
+     // The following functions create the actual matcher objects.
+     // This allows the types to be inferred
+ 
+-    template<typename T>
+-    Vector::ContainsMatcher<T> Contains( std::vector<T> const& comparator ) {
+-        return Vector::ContainsMatcher<T>( comparator );
++    template<typename T, typename AllocComp = std::allocator<T>, typename AllocMatch = AllocComp>
++    Vector::ContainsMatcher<T, AllocComp, AllocMatch> Contains( std::vector<T, AllocComp> const& comparator ) {
++        return Vector::ContainsMatcher<T, AllocComp, AllocMatch>( comparator );
+     }
+ 
+-    template<typename T>
+-    Vector::ContainsElementMatcher<T> VectorContains( T const& comparator ) {
+-        return Vector::ContainsElementMatcher<T>( comparator );
++    template<typename T, typename Alloc = std::allocator<T>>
++    Vector::ContainsElementMatcher<T, Alloc> VectorContains( T const& comparator ) {
++        return Vector::ContainsElementMatcher<T, Alloc>( comparator );
+     }
+ 
+-    template<typename T>
+-    Vector::EqualsMatcher<T> Equals( std::vector<T> const& comparator ) {
+-        return Vector::EqualsMatcher<T>( comparator );
++    template<typename T, typename AllocComp = std::allocator<T>, typename AllocMatch = AllocComp>
++    Vector::EqualsMatcher<T, AllocComp, AllocMatch> Equals( std::vector<T, AllocComp> const& comparator ) {
++        return Vector::EqualsMatcher<T, AllocComp, AllocMatch>( comparator );
+     }
+ 
+-    template<typename T>
+-    Vector::UnorderedEqualsMatcher<T> UnorderedEquals(std::vector<T> const& target) {
+-        return Vector::UnorderedEqualsMatcher<T>(target);
++    template<typename T, typename AllocComp = std::allocator<T>, typename AllocMatch = AllocComp>
++    Vector::ApproxMatcher<T, AllocComp, AllocMatch> Approx( std::vector<T, AllocComp> const& comparator ) {
++        return Vector::ApproxMatcher<T, AllocComp, AllocMatch>( comparator );
++    }
++
++    template<typename T, typename AllocComp = std::allocator<T>, typename AllocMatch = AllocComp>
++    Vector::UnorderedEqualsMatcher<T, AllocComp, AllocMatch> UnorderedEquals(std::vector<T, AllocComp> const& target) {
++        return Vector::UnorderedEqualsMatcher<T, AllocComp, AllocMatch>( target );
+     }
+ 
+ } // namespace Matchers
+@@ -3175,16 +3842,17 @@
+ namespace Catch {
+ 
+     namespace Generators {
+-        class GeneratorBase {
+-        protected:
+-            size_t m_size = 0;
+-
++        class GeneratorUntypedBase {
+         public:
+-            GeneratorBase( size_t size ) : m_size( size ) {}
+-            virtual ~GeneratorBase();
+-            auto size() const -> size_t { return m_size; }
++            GeneratorUntypedBase() = default;
++            virtual ~GeneratorUntypedBase();
++            // Attempts to move the generator to the next element
++             //
++             // Returns true iff the move succeeded (and a valid element
++             // can be retrieved).
++            virtual bool next() = 0;
+         };
+-        using GeneratorBasePtr = std::unique_ptr<GeneratorBase>;
++        using GeneratorBasePtr = std::unique_ptr<GeneratorUntypedBase>;
+ 
+     } // namespace Generators
+ 
+@@ -3193,7 +3861,6 @@
+         virtual auto hasGenerator() const -> bool = 0;
+         virtual auto getGenerator() const -> Generators::GeneratorBasePtr const& = 0;
+         virtual void setGenerator( Generators::GeneratorBasePtr&& generator ) = 0;
+-        virtual auto getIndex() const -> std::size_t = 0;
+     };
+ 
+ } // namespace Catch
+@@ -3201,7 +3868,7 @@
+ // end catch_interfaces_generatortracker.h
+ // start catch_enforce.h
+ 
+-#include <stdexcept>
++#include <exception>
+ 
+ namespace Catch {
+ #if !defined(CATCH_CONFIG_DISABLE_EXCEPTIONS)
+@@ -3214,18 +3881,30 @@
+     [[noreturn]]
+     void throw_exception(std::exception const& e);
+ #endif
++
++    [[noreturn]]
++    void throw_logic_error(std::string const& msg);
++    [[noreturn]]
++    void throw_domain_error(std::string const& msg);
++    [[noreturn]]
++    void throw_runtime_error(std::string const& msg);
++
+ } // namespace Catch;
+ 
+-#define CATCH_PREPARE_EXCEPTION( type, msg ) \
+-    type( ( Catch::ReusableStringStream() << msg ).str() )
+-#define CATCH_INTERNAL_ERROR( msg ) \
+-    Catch::throw_exception(CATCH_PREPARE_EXCEPTION( std::logic_error, CATCH_INTERNAL_LINEINFO << ": Internal Catch error: " << msg))
+-#define CATCH_ERROR( msg ) \
+-    Catch::throw_exception(CATCH_PREPARE_EXCEPTION( std::domain_error, msg ))
+-#define CATCH_RUNTIME_ERROR( msg ) \
+-    Catch::throw_exception(CATCH_PREPARE_EXCEPTION( std::runtime_error, msg ))
+-#define CATCH_ENFORCE( condition, msg ) \
+-    do{ if( !(condition) ) CATCH_ERROR( msg ); } while(false)
++#define CATCH_MAKE_MSG(...) \
++    (Catch::ReusableStringStream() << __VA_ARGS__).str()
++
++#define CATCH_INTERNAL_ERROR(...) \
++    Catch::throw_logic_error(CATCH_MAKE_MSG( CATCH_INTERNAL_LINEINFO << ": Internal Catch2 error: " << __VA_ARGS__))
++
++#define CATCH_ERROR(...) \
++    Catch::throw_domain_error(CATCH_MAKE_MSG( __VA_ARGS__ ))
++
++#define CATCH_RUNTIME_ERROR(...) \
++    Catch::throw_runtime_error(CATCH_MAKE_MSG( __VA_ARGS__ ))
++
++#define CATCH_ENFORCE( condition, ... ) \
++    do{ if( !(condition) ) CATCH_ERROR( __VA_ARGS__ ); } while(false)
+ 
+ // end catch_enforce.h
+ #include <memory>
+@@ -3233,8 +3912,21 @@
+ #include <cassert>
+ 
+ #include <utility>
++#include <exception>
+ 
+ namespace Catch {
++
++class GeneratorException : public std::exception {
++    const char* const m_msg = "";
++
++public:
++    GeneratorException(const char* msg):
++        m_msg(msg)
++    {}
++
++    const char* what() const noexcept override final;
++};
++
+ namespace Generators {
+ 
+     // !TBD move this into its own location?
+@@ -3246,223 +3938,819 @@
+     }
+ 
+     template<typename T>
+-    struct IGenerator {
+-        virtual ~IGenerator() {}
+-        virtual auto get( size_t index ) const -> T = 0;
++    struct IGenerator : GeneratorUntypedBase {
++        virtual ~IGenerator() = default;
++
++        // Returns the current element of the generator
++        //
++        // \Precondition The generator is either freshly constructed,
++        // or the last call to `next()` returned true
++        virtual T const& get() const = 0;
++        using type = T;
+     };
+ 
+     template<typename T>
+-    class SingleValueGenerator : public IGenerator<T> {
++    class SingleValueGenerator final : public IGenerator<T> {
+         T m_value;
+     public:
+-        SingleValueGenerator( T const& value ) : m_value( value ) {}
++        SingleValueGenerator(T&& value) : m_value(std::move(value)) {}
+ 
+-        auto get( size_t ) const -> T override {
++        T const& get() const override {
+             return m_value;
+         }
++        bool next() override {
++            return false;
++        }
+     };
+ 
+     template<typename T>
+-    class FixedValuesGenerator : public IGenerator<T> {
++    class FixedValuesGenerator final : public IGenerator<T> {
++        static_assert(!std::is_same<T, bool>::value,
++            "FixedValuesGenerator does not support bools because of std::vector<bool>"
++            "specialization, use SingleValue Generator instead.");
+         std::vector<T> m_values;
+-
++        size_t m_idx = 0;
+     public:
+         FixedValuesGenerator( std::initializer_list<T> values ) : m_values( values ) {}
+ 
+-        auto get( size_t index ) const -> T override {
+-            return m_values[index];
++        T const& get() const override {
++            return m_values[m_idx];
++        }
++        bool next() override {
++            ++m_idx;
++            return m_idx < m_values.size();
++        }
++    };
++
++    template <typename T>
++    class GeneratorWrapper final {
++        std::unique_ptr<IGenerator<T>> m_generator;
++    public:
++        GeneratorWrapper(std::unique_ptr<IGenerator<T>> generator):
++            m_generator(std::move(generator))
++        {}
++        T const& get() const {
++            return m_generator->get();
++        }
++        bool next() {
++            return m_generator->next();
+         }
+     };
+ 
++    template <typename T>
++    GeneratorWrapper<T> value(T&& value) {
++        return GeneratorWrapper<T>(pf::make_unique<SingleValueGenerator<T>>(std::forward<T>(value)));
++    }
++    template <typename T>
++    GeneratorWrapper<T> values(std::initializer_list<T> values) {
++        return GeneratorWrapper<T>(pf::make_unique<FixedValuesGenerator<T>>(values));
++    }
++
+     template<typename T>
+-    class RangeGenerator : public IGenerator<T> {
+-        T const m_first;
+-        T const m_last;
++    class Generators : public IGenerator<T> {
++        std::vector<GeneratorWrapper<T>> m_generators;
++        size_t m_current = 0;
++
++        void populate(GeneratorWrapper<T>&& generator) {
++            m_generators.emplace_back(std::move(generator));
++        }
++        void populate(T&& val) {
++            m_generators.emplace_back(value(std::forward<T>(val)));
++        }
++        template<typename U>
++        void populate(U&& val) {
++            populate(T(std::forward<U>(val)));
++        }
++        template<typename U, typename... Gs>
++        void populate(U&& valueOrGenerator, Gs &&... moreGenerators) {
++            populate(std::forward<U>(valueOrGenerator));
++            populate(std::forward<Gs>(moreGenerators)...);
++        }
+ 
+     public:
+-        RangeGenerator( T const& first, T const& last ) : m_first( first ), m_last( last ) {
+-            assert( m_last > m_first );
++        template <typename... Gs>
++        Generators(Gs &&... moreGenerators) {
++            m_generators.reserve(sizeof...(Gs));
++            populate(std::forward<Gs>(moreGenerators)...);
+         }
+ 
+-        auto get( size_t index ) const -> T override {
+-            // ToDo:: introduce a safe cast to catch potential overflows
+-            return static_cast<T>(m_first+index);
++        T const& get() const override {
++            return m_generators[m_current].get();
+         }
+-    };
+ 
+-    template<typename T>
+-    struct NullGenerator : IGenerator<T> {
+-        auto get( size_t ) const -> T override {
+-            CATCH_INTERNAL_ERROR("A Null Generator is always empty");
++        bool next() override {
++            if (m_current >= m_generators.size()) {
++                return false;
++            }
++            const bool current_status = m_generators[m_current].next();
++            if (!current_status) {
++                ++m_current;
++            }
++            return m_current < m_generators.size();
+         }
+     };
+ 
++    template<typename... Ts>
++    GeneratorWrapper<std::tuple<Ts...>> table( std::initializer_list<std::tuple<typename std::decay<Ts>::type...>> tuples ) {
++        return values<std::tuple<Ts...>>( tuples );
++    }
++
++    // Tag type to signal that a generator sequence should convert arguments to a specific type
++    template <typename T>
++    struct as {};
++
++    template<typename T, typename... Gs>
++    auto makeGenerators( GeneratorWrapper<T>&& generator, Gs &&... moreGenerators ) -> Generators<T> {
++        return Generators<T>(std::move(generator), std::forward<Gs>(moreGenerators)...);
++    }
+     template<typename T>
+-    class Generator {
+-        std::unique_ptr<IGenerator<T>> m_generator;
+-        size_t m_size;
++    auto makeGenerators( GeneratorWrapper<T>&& generator ) -> Generators<T> {
++        return Generators<T>(std::move(generator));
++    }
++    template<typename T, typename... Gs>
++    auto makeGenerators( T&& val, Gs &&... moreGenerators ) -> Generators<T> {
++        return makeGenerators( value( std::forward<T>( val ) ), std::forward<Gs>( 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 )... );
++    }
+ 
+-    public:
+-        Generator( size_t size, std::unique_ptr<IGenerator<T>> generator )
+-        :   m_generator( std::move( generator ) ),
+-            m_size( size )
+-        {}
++    auto acquireGeneratorTracker( StringRef generatorName, SourceLineInfo const& lineInfo ) -> IGeneratorTracker&;
+ 
+-        auto size() const -> size_t { return m_size; }
+-        auto operator[]( size_t index ) const -> T {
+-            assert( index < m_size );
+-            return m_generator->get( index );
++    template<typename L>
++    // Note: The type after -> is weird, because VS2015 cannot parse
++    //       the expression used in the typedef inside, when it is in
++    //       return type. Yeah.
++    auto generate( StringRef generatorName, SourceLineInfo const& lineInfo, L const& generatorExpression ) -> decltype(std::declval<decltype(generatorExpression())>().get()) {
++        using UnderlyingType = typename decltype(generatorExpression())::type;
++
++        IGeneratorTracker& tracker = acquireGeneratorTracker( generatorName, lineInfo );
++        if (!tracker.hasGenerator()) {
++            tracker.setGenerator(pf::make_unique<Generators<UnderlyingType>>(generatorExpression()));
+         }
+-    };
+ 
+-    std::vector<size_t> randomiseIndices( size_t selectionSize, size_t sourceSize );
++        auto const& generator = static_cast<IGenerator<UnderlyingType> const&>( *tracker.getGenerator() );
++        return generator.get();
++    }
++
++} // namespace Generators
++} // namespace Catch
+ 
+-    template<typename T>
+-    class GeneratorRandomiser : public IGenerator<T> {
+-        Generator<T> m_baseGenerator;
++#define GENERATE( ... ) \
++    Catch::Generators::generate( INTERNAL_CATCH_STRINGIZE(INTERNAL_CATCH_UNIQUE_NAME(generator)), \
++                                 CATCH_INTERNAL_LINEINFO, \
++                                 [ ]{ using namespace Catch::Generators; return makeGenerators( __VA_ARGS__ ); } ) //NOLINT(google-build-using-namespace)
++#define GENERATE_COPY( ... ) \
++    Catch::Generators::generate( INTERNAL_CATCH_STRINGIZE(INTERNAL_CATCH_UNIQUE_NAME(generator)), \
++                                 CATCH_INTERNAL_LINEINFO, \
++                                 [=]{ using namespace Catch::Generators; return makeGenerators( __VA_ARGS__ ); } ) //NOLINT(google-build-using-namespace)
++#define GENERATE_REF( ... ) \
++    Catch::Generators::generate( INTERNAL_CATCH_STRINGIZE(INTERNAL_CATCH_UNIQUE_NAME(generator)), \
++                                 CATCH_INTERNAL_LINEINFO, \
++                                 [&]{ using namespace Catch::Generators; return makeGenerators( __VA_ARGS__ ); } ) //NOLINT(google-build-using-namespace)
++
++// end catch_generators.hpp
++// start catch_generators_generic.hpp
++
++namespace Catch {
++namespace Generators {
+ 
+-        std::vector<size_t> m_indices;
++    template <typename T>
++    class TakeGenerator : public IGenerator<T> {
++        GeneratorWrapper<T> m_generator;
++        size_t m_returned = 0;
++        size_t m_target;
+     public:
+-        GeneratorRandomiser( Generator<T>&& baseGenerator, size_t numberOfItems )
+-        :   m_baseGenerator( std::move( baseGenerator ) ),
+-            m_indices( randomiseIndices( numberOfItems, m_baseGenerator.size() ) )
+-        {}
++        TakeGenerator(size_t target, GeneratorWrapper<T>&& generator):
++            m_generator(std::move(generator)),
++            m_target(target)
++        {
++            assert(target != 0 && "Empty generators are not allowed");
++        }
++        T const& get() const override {
++            return m_generator.get();
++        }
++        bool next() override {
++            ++m_returned;
++            if (m_returned >= m_target) {
++                return false;
++            }
+ 
+-        auto get( size_t index ) const -> T override {
+-            return m_baseGenerator[m_indices[index]];
++            const auto success = m_generator.next();
++            // If the underlying generator does not contain enough values
++            // then we cut short as well
++            if (!success) {
++                m_returned = m_target;
++            }
++            return success;
+         }
+     };
+ 
+-    template<typename T>
+-    struct RequiresASpecialisationFor;
++    template <typename T>
++    GeneratorWrapper<T> take(size_t target, GeneratorWrapper<T>&& generator) {
++        return GeneratorWrapper<T>(pf::make_unique<TakeGenerator<T>>(target, std::move(generator)));
++    }
+ 
+-    template<typename T>
+-    auto all() -> Generator<T> { return RequiresASpecialisationFor<T>(); }
++    template <typename T, typename Predicate>
++    class FilterGenerator : public IGenerator<T> {
++        GeneratorWrapper<T> m_generator;
++        Predicate m_predicate;
++    public:
++        template <typename P = Predicate>
++        FilterGenerator(P&& pred, GeneratorWrapper<T>&& generator):
++            m_generator(std::move(generator)),
++            m_predicate(std::forward<P>(pred))
++        {
++            if (!m_predicate(m_generator.get())) {
++                // It might happen that there are no values that pass the
++                // filter. In that case we throw an exception.
++                auto has_initial_value = nextImpl();
++                if (!has_initial_value) {
++                    Catch::throw_exception(GeneratorException("No valid value found in filtered generator"));
++                }
++            }
++        }
+ 
+-    template<>
+-    auto all<int>() -> Generator<int>;
++        T const& get() const override {
++            return m_generator.get();
++        }
+ 
+-    template<typename T>
+-    auto range( T const& first, T const& last ) -> Generator<T> {
+-        return Generator<T>( (last-first), pf::make_unique<RangeGenerator<T>>( first, last ) );
++        bool next() override {
++            return nextImpl();
++        }
++
++    private:
++        bool nextImpl() {
++            bool success = m_generator.next();
++            if (!success) {
++                return false;
++            }
++            while (!m_predicate(m_generator.get()) && (success = m_generator.next()) == true);
++            return success;
++        }
++    };
++
++    template <typename T, typename Predicate>
++    GeneratorWrapper<T> filter(Predicate&& pred, GeneratorWrapper<T>&& generator) {
++        return GeneratorWrapper<T>(std::unique_ptr<IGenerator<T>>(pf::make_unique<FilterGenerator<T, Predicate>>(std::forward<Predicate>(pred), std::move(generator))));
+     }
+ 
+-    template<typename T>
+-    auto random( T const& first, T const& last ) -> Generator<T> {
+-        auto gen = range( first, last );
+-        auto size = gen.size();
++    template <typename T>
++    class RepeatGenerator : public IGenerator<T> {
++        static_assert(!std::is_same<T, bool>::value,
++            "RepeatGenerator currently does not support bools"
++            "because of std::vector<bool> specialization");
++        GeneratorWrapper<T> m_generator;
++        mutable std::vector<T> m_returned;
++        size_t m_target_repeats;
++        size_t m_current_repeat = 0;
++        size_t m_repeat_index = 0;
++    public:
++        RepeatGenerator(size_t repeats, GeneratorWrapper<T>&& generator):
++            m_generator(std::move(generator)),
++            m_target_repeats(repeats)
++        {
++            assert(m_target_repeats > 0 && "Repeat generator must repeat at least once");
++        }
++
++        T const& get() const override {
++            if (m_current_repeat == 0) {
++                m_returned.push_back(m_generator.get());
++                return m_returned.back();
++            }
++            return m_returned[m_repeat_index];
++        }
++
++        bool next() override {
++            // There are 2 basic cases:
++            // 1) We are still reading the generator
++            // 2) We are reading our own cache
++
++            // In the first case, we need to poke the underlying generator.
++            // If it happily moves, we are left in that state, otherwise it is time to start reading from our cache
++            if (m_current_repeat == 0) {
++                const auto success = m_generator.next();
++                if (!success) {
++                    ++m_current_repeat;
++                }
++                return m_current_repeat < m_target_repeats;
++            }
++
++            // In the second case, we need to move indices forward and check that we haven't run up against the end
++            ++m_repeat_index;
++            if (m_repeat_index == m_returned.size()) {
++                m_repeat_index = 0;
++                ++m_current_repeat;
++            }
++            return m_current_repeat < m_target_repeats;
++        }
++    };
+ 
+-        return Generator<T>( size, pf::make_unique<GeneratorRandomiser<T>>( std::move( gen ), size ) );
++    template <typename T>
++    GeneratorWrapper<T> repeat(size_t repeats, GeneratorWrapper<T>&& generator) {
++        return GeneratorWrapper<T>(pf::make_unique<RepeatGenerator<T>>(repeats, std::move(generator)));
+     }
+-    template<typename T>
+-    auto random( size_t size ) -> Generator<T> {
+-        return Generator<T>( size, pf::make_unique<GeneratorRandomiser<T>>( all<T>(), size ) );
++
++    template <typename T, typename U, typename Func>
++    class MapGenerator : public IGenerator<T> {
++        // TBD: provide static assert for mapping function, for friendly error message
++        GeneratorWrapper<U> m_generator;
++        Func m_function;
++        // To avoid returning dangling reference, we have to save the values
++        T m_cache;
++    public:
++        template <typename F2 = Func>
++        MapGenerator(F2&& function, GeneratorWrapper<U>&& generator) :
++            m_generator(std::move(generator)),
++            m_function(std::forward<F2>(function)),
++            m_cache(m_function(m_generator.get()))
++        {}
++
++        T const& get() const override {
++            return m_cache;
++        }
++        bool next() override {
++            const auto success = m_generator.next();
++            if (success) {
++                m_cache = m_function(m_generator.get());
++            }
++            return success;
++        }
++    };
++
++    template <typename Func, typename U, typename T = FunctionReturnType<Func, U>>
++    GeneratorWrapper<T> map(Func&& function, GeneratorWrapper<U>&& generator) {
++        return GeneratorWrapper<T>(
++            pf::make_unique<MapGenerator<T, U, Func>>(std::forward<Func>(function), std::move(generator))
++        );
+     }
+ 
+-    template<typename T>
+-    auto values( std::initializer_list<T> values ) -> Generator<T> {
+-        return Generator<T>( values.size(), pf::make_unique<FixedValuesGenerator<T>>( values ) );
++    template <typename T, typename U, typename Func>
++    GeneratorWrapper<T> map(Func&& function, GeneratorWrapper<U>&& generator) {
++        return GeneratorWrapper<T>(
++            pf::make_unique<MapGenerator<T, U, Func>>(std::forward<Func>(function), std::move(generator))
++        );
+     }
+-    template<typename T>
+-    auto value( T const& val ) -> Generator<T> {
+-        return Generator<T>( 1, pf::make_unique<SingleValueGenerator<T>>( val ) );
++
++    template <typename T>
++    class ChunkGenerator final : public IGenerator<std::vector<T>> {
++        std::vector<T> m_chunk;
++        size_t m_chunk_size;
++        GeneratorWrapper<T> m_generator;
++        bool m_used_up = false;
++    public:
++        ChunkGenerator(size_t size, GeneratorWrapper<T> generator) :
++            m_chunk_size(size), m_generator(std::move(generator))
++        {
++            m_chunk.reserve(m_chunk_size);
++            if (m_chunk_size != 0) {
++                m_chunk.push_back(m_generator.get());
++                for (size_t i = 1; i < m_chunk_size; ++i) {
++                    if (!m_generator.next()) {
++                        Catch::throw_exception(GeneratorException("Not enough values to initialize the first chunk"));
++                    }
++                    m_chunk.push_back(m_generator.get());
++                }
++            }
++        }
++        std::vector<T> const& get() const override {
++            return m_chunk;
++        }
++        bool next() override {
++            m_chunk.clear();
++            for (size_t idx = 0; idx < m_chunk_size; ++idx) {
++                if (!m_generator.next()) {
++                    return false;
++                }
++                m_chunk.push_back(m_generator.get());
++            }
++            return true;
++        }
++    };
++
++    template <typename T>
++    GeneratorWrapper<std::vector<T>> chunk(size_t size, GeneratorWrapper<T>&& generator) {
++        return GeneratorWrapper<std::vector<T>>(
++            pf::make_unique<ChunkGenerator<T>>(size, std::move(generator))
++        );
+     }
+ 
+-    template<typename T>
+-    auto as() -> Generator<T> {
+-        return Generator<T>( 0, pf::make_unique<NullGenerator<T>>() );
++} // namespace Generators
++} // namespace Catch
++
++// end catch_generators_generic.hpp
++// start catch_generators_specific.hpp
++
++// start catch_context.h
++
++#include <memory>
++
++namespace Catch {
++
++    struct IResultCapture;
++    struct IRunner;
++    struct IConfig;
++    struct IMutableContext;
++
++    using IConfigPtr = std::shared_ptr<IConfig const>;
++
++    struct IContext
++    {
++        virtual ~IContext();
++
++        virtual IResultCapture* getResultCapture() = 0;
++        virtual IRunner* getRunner() = 0;
++        virtual IConfigPtr const& getConfig() const = 0;
++    };
++
++    struct IMutableContext : IContext
++    {
++        virtual ~IMutableContext();
++        virtual void setResultCapture( IResultCapture* resultCapture ) = 0;
++        virtual void setRunner( IRunner* runner ) = 0;
++        virtual void setConfig( IConfigPtr const& config ) = 0;
++
++    private:
++        static IMutableContext *currentContext;
++        friend IMutableContext& getCurrentMutableContext();
++        friend void cleanUpContext();
++        static void createContext();
++    };
++
++    inline IMutableContext& getCurrentMutableContext()
++    {
++        if( !IMutableContext::currentContext )
++            IMutableContext::createContext();
++        // NOLINTNEXTLINE(clang-analyzer-core.uninitialized.UndefReturn)
++        return *IMutableContext::currentContext;
+     }
+ 
+-    template<typename... Ts>
+-    auto table( std::initializer_list<std::tuple<Ts...>>&& tuples ) -> Generator<std::tuple<Ts...>> {
+-        return values<std::tuple<Ts...>>( std::forward<std::initializer_list<std::tuple<Ts...>>>( tuples ) );
++    inline IContext& getCurrentContext()
++    {
++        return getCurrentMutableContext();
+     }
+ 
+-    template<typename T>
+-    struct Generators : GeneratorBase {
+-        std::vector<Generator<T>> m_generators;
++    void cleanUpContext();
+ 
+-        using type = T;
++    class SimplePcg32;
++    SimplePcg32& rng();
++}
+ 
+-        Generators() : GeneratorBase( 0 ) {}
++// end catch_context.h
++// start catch_interfaces_config.h
+ 
+-        void populate( T&& val ) {
+-            m_size += 1;
+-            m_generators.emplace_back( value( std::move( val ) ) );
++// start catch_option.hpp
++
++namespace Catch {
++
++    // An optional type
++    template<typename T>
++    class Option {
++    public:
++        Option() : nullableValue( nullptr ) {}
++        Option( T const& _value )
++        : nullableValue( new( storage ) T( _value ) )
++        {}
++        Option( Option const& _other )
++        : nullableValue( _other ? new( storage ) T( *_other ) : nullptr )
++        {}
++
++        ~Option() {
++            reset();
+         }
+-        template<typename U>
+-        void populate( U&& val ) {
+-            populate( T( std::move( val ) ) );
++
++        Option& operator= ( Option const& _other ) {
++            if( &_other != this ) {
++                reset();
++                if( _other )
++                    nullableValue = new( storage ) T( *_other );
++            }
++            return *this;
+         }
+-        void populate( Generator<T>&& generator ) {
+-            m_size += generator.size();
+-            m_generators.emplace_back( std::move( generator ) );
++        Option& operator = ( T const& _value ) {
++            reset();
++            nullableValue = new( storage ) T( _value );
++            return *this;
+         }
+ 
+-        template<typename U, typename... Gs>
+-        void populate( U&& valueOrGenerator, Gs... moreGenerators ) {
+-            populate( std::forward<U>( valueOrGenerator ) );
+-            populate( std::forward<Gs>( moreGenerators )... );
++        void reset() {
++            if( nullableValue )
++                nullableValue->~T();
++            nullableValue = nullptr;
+         }
+ 
+-        auto operator[]( size_t index ) const -> T {
+-            size_t sizes = 0;
+-            for( auto const& gen : m_generators ) {
+-                auto localIndex = index-sizes;
+-                sizes += gen.size();
+-                if( index < sizes )
+-                    return gen[localIndex];
+-            }
+-            CATCH_INTERNAL_ERROR("Index '" << index << "' is out of range (" << sizes << ')');
++        T& operator*() { return *nullableValue; }
++        T const& operator*() const { return *nullableValue; }
++        T* operator->() { return nullableValue; }
++        const T* operator->() const { return nullableValue; }
++
++        T valueOr( T const& defaultValue ) const {
++            return nullableValue ? *nullableValue : defaultValue;
++        }
++
++        bool some() const { return nullableValue != nullptr; }
++        bool none() const { return nullableValue == nullptr; }
++
++        bool operator !() const { return nullableValue == nullptr; }
++        explicit operator bool() const {
++            return some();
+         }
++
++    private:
++        T *nullableValue;
++        alignas(alignof(T)) char storage[sizeof(T)];
+     };
+ 
+-    template<typename T, typename... Gs>
+-    auto makeGenerators( Generator<T>&& generator, Gs... moreGenerators ) -> Generators<T> {
+-        Generators<T> generators;
+-        generators.m_generators.reserve( 1+sizeof...(Gs) );
+-        generators.populate( std::move( generator ), std::forward<Gs>( moreGenerators )... );
+-        return generators;
++} // end namespace Catch
++
++// end catch_option.hpp
++#include <chrono>
++#include <iosfwd>
++#include <string>
++#include <vector>
++#include <memory>
++
++namespace Catch {
++
++    enum class Verbosity {
++        Quiet = 0,
++        Normal,
++        High
++    };
++
++    struct WarnAbout { enum What {
++        Nothing = 0x00,
++        NoAssertions = 0x01,
++        NoTests = 0x02
++    }; };
++
++    struct ShowDurations { enum OrNot {
++        DefaultForReporter,
++        Always,
++        Never
++    }; };
++    struct RunTests { enum InWhatOrder {
++        InDeclarationOrder,
++        InLexicographicalOrder,
++        InRandomOrder
++    }; };
++    struct UseColour { enum YesOrNo {
++        Auto,
++        Yes,
++        No
++    }; };
++    struct WaitForKeypress { enum When {
++        Never,
++        BeforeStart = 1,
++        BeforeExit = 2,
++        BeforeStartAndExit = BeforeStart | BeforeExit
++    }; };
++
++    class TestSpec;
++
++    struct IConfig : NonCopyable {
++
++        virtual ~IConfig();
++
++        virtual bool allowThrows() const = 0;
++        virtual std::ostream& stream() const = 0;
++        virtual std::string name() const = 0;
++        virtual bool includeSuccessfulResults() const = 0;
++        virtual bool shouldDebugBreak() const = 0;
++        virtual bool warnAboutMissingAssertions() const = 0;
++        virtual bool warnAboutNoTests() const = 0;
++        virtual int abortAfter() const = 0;
++        virtual bool showInvisibles() const = 0;
++        virtual ShowDurations::OrNot showDurations() const = 0;
++        virtual double minDuration() const = 0;
++        virtual TestSpec const& testSpec() const = 0;
++        virtual bool hasTestFilters() const = 0;
++        virtual std::vector<std::string> const& getTestsOrTags() const = 0;
++        virtual RunTests::InWhatOrder runOrder() const = 0;
++        virtual unsigned int rngSeed() const = 0;
++        virtual UseColour::YesOrNo useColour() const = 0;
++        virtual std::vector<std::string> const& getSectionsToRun() const = 0;
++        virtual Verbosity verbosity() const = 0;
++
++        virtual bool benchmarkNoAnalysis() const = 0;
++        virtual int benchmarkSamples() const = 0;
++        virtual double benchmarkConfidenceInterval() const = 0;
++        virtual unsigned int benchmarkResamples() const = 0;
++        virtual std::chrono::milliseconds benchmarkWarmupTime() const = 0;
++    };
++
++    using IConfigPtr = std::shared_ptr<IConfig const>;
++}
++
++// end catch_interfaces_config.h
++// start catch_random_number_generator.h
++
++#include <cstdint>
++
++namespace Catch {
++
++    // This is a simple implementation of C++11 Uniform Random Number
++    // Generator. It does not provide all operators, because Catch2
++    // does not use it, but it should behave as expected inside stdlib's
++    // distributions.
++    // The implementation is based on the PCG family (http://pcg-random.org)
++    class SimplePcg32 {
++        using state_type = std::uint64_t;
++    public:
++        using result_type = std::uint32_t;
++        static constexpr result_type (min)() {
++            return 0;
++        }
++        static constexpr result_type (max)() {
++            return static_cast<result_type>(-1);
++        }
++
++        // Provide some default initial state for the default constructor
++        SimplePcg32():SimplePcg32(0xed743cc4U) {}
++
++        explicit SimplePcg32(result_type seed_);
++
++        void seed(result_type seed_);
++        void discard(uint64_t skip);
++
++        result_type operator()();
++
++    private:
++        friend bool operator==(SimplePcg32 const& lhs, SimplePcg32 const& rhs);
++        friend bool operator!=(SimplePcg32 const& lhs, SimplePcg32 const& rhs);
++
++        // In theory we also need operator<< and operator>>
++        // In practice we do not use them, so we will skip them for now
++
++        std::uint64_t m_state;
++        // This part of the state determines which "stream" of the numbers
++        // is chosen -- we take it as a constant for Catch2, so we only
++        // need to deal with seeding the main state.
++        // Picked by reading 8 bytes from `/dev/random` :-)
++        static const std::uint64_t s_inc = (0x13ed0cc53f939476ULL << 1ULL) | 1ULL;
++    };
++
++} // end namespace Catch
++
++// end catch_random_number_generator.h
++#include <random>
++
++namespace Catch {
++namespace Generators {
++
++template <typename Float>
++class RandomFloatingGenerator final : public IGenerator<Float> {
++    Catch::SimplePcg32& m_rng;
++    std::uniform_real_distribution<Float> m_dist;
++    Float m_current_number;
++public:
++
++    RandomFloatingGenerator(Float a, Float b):
++        m_rng(rng()),
++        m_dist(a, b) {
++        static_cast<void>(next());
+     }
+-    template<typename T>
+-    auto makeGenerators( Generator<T>&& generator ) -> Generators<T> {
+-        Generators<T> generators;
+-        generators.populate( std::move( generator ) );
+-        return generators;
++
++    Float const& get() const override {
++        return m_current_number;
+     }
+-    template<typename T, typename... Gs>
+-    auto makeGenerators( T&& val, Gs... moreGenerators ) -> Generators<T> {
+-        return makeGenerators( value( std::forward<T>( val ) ), std::forward<Gs>( moreGenerators )... );
++    bool next() override {
++        m_current_number = m_dist(m_rng);
++        return true;
+     }
+-    template<typename T, typename U, typename... Gs>
+-    auto makeGenerators( U&& val, Gs... moreGenerators ) -> Generators<T> {
+-        return makeGenerators( value( T( std::forward<U>( val ) ) ), std::forward<Gs>( moreGenerators )... );
++};
++
++template <typename Integer>
++class RandomIntegerGenerator final : public IGenerator<Integer> {
++    Catch::SimplePcg32& m_rng;
++    std::uniform_int_distribution<Integer> m_dist;
++    Integer m_current_number;
++public:
++
++    RandomIntegerGenerator(Integer a, Integer b):
++        m_rng(rng()),
++        m_dist(a, b) {
++        static_cast<void>(next());
++    }
++
++    Integer const& get() const override {
++        return m_current_number;
++    }
++    bool next() override {
++        m_current_number = m_dist(m_rng);
++        return true;
+     }
++};
+ 
+-    auto acquireGeneratorTracker( SourceLineInfo const& lineInfo ) -> IGeneratorTracker&;
++// TODO: Ideally this would be also constrained against the various char types,
++//       but I don't expect users to run into that in practice.
++template <typename T>
++typename std::enable_if<std::is_integral<T>::value && !std::is_same<T, bool>::value,
++GeneratorWrapper<T>>::type
++random(T a, T b) {
++    return GeneratorWrapper<T>(
++        pf::make_unique<RandomIntegerGenerator<T>>(a, b)
++    );
++}
+ 
+-    template<typename L>
+-    // Note: The type after -> is weird, because VS2015 cannot parse
+-    //       the expression used in the typedef inside, when it is in
+-    //       return type. Yeah, ¯\_(ツ)_/¯
+-    auto generate( SourceLineInfo const& lineInfo, L const& generatorExpression ) -> decltype(std::declval<decltype(generatorExpression())>()[0]) {
+-        using UnderlyingType = typename decltype(generatorExpression())::type;
++template <typename T>
++typename std::enable_if<std::is_floating_point<T>::value,
++GeneratorWrapper<T>>::type
++random(T a, T b) {
++    return GeneratorWrapper<T>(
++        pf::make_unique<RandomFloatingGenerator<T>>(a, b)
++    );
++}
++
++template <typename T>
++class RangeGenerator final : public IGenerator<T> {
++    T m_current;
++    T m_end;
++    T m_step;
++    bool m_positive;
++
++public:
++    RangeGenerator(T const& start, T const& end, T const& step):
++        m_current(start),
++        m_end(end),
++        m_step(step),
++        m_positive(m_step > T(0))
++    {
++        assert(m_current != m_end && "Range start and end cannot be equal");
++        assert(m_step != T(0) && "Step size cannot be zero");
++        assert(((m_positive && m_current <= m_end) || (!m_positive && m_current >= m_end)) && "Step moves away from end");
++    }
++
++    RangeGenerator(T const& start, T const& end):
++        RangeGenerator(start, end, (start < end) ? T(1) : T(-1))
++    {}
++
++    T const& get() const override {
++        return m_current;
++    }
++
++    bool next() override {
++        m_current += m_step;
++        return (m_positive) ? (m_current < m_end) : (m_current > m_end);
++    }
++};
++
++template <typename T>
++GeneratorWrapper<T> range(T const& start, T const& end, T const& step) {
++    static_assert(std::is_arithmetic<T>::value && !std::is_same<T, bool>::value, "Type must be numeric");
++    return GeneratorWrapper<T>(pf::make_unique<RangeGenerator<T>>(start, end, step));
++}
++
++template <typename T>
++GeneratorWrapper<T> range(T const& start, T const& end) {
++    static_assert(std::is_integral<T>::value && !std::is_same<T, bool>::value, "Type must be an integer");
++    return GeneratorWrapper<T>(pf::make_unique<RangeGenerator<T>>(start, end));
++}
+ 
+-        IGeneratorTracker& tracker = acquireGeneratorTracker( lineInfo );
+-        if( !tracker.hasGenerator() )
+-            tracker.setGenerator( pf::make_unique<Generators<UnderlyingType>>( generatorExpression() ) );
++template <typename T>
++class IteratorGenerator final : public IGenerator<T> {
++    static_assert(!std::is_same<T, bool>::value,
++        "IteratorGenerator currently does not support bools"
++        "because of std::vector<bool> specialization");
++
++    std::vector<T> m_elems;
++    size_t m_current = 0;
++public:
++    template <typename InputIterator, typename InputSentinel>
++    IteratorGenerator(InputIterator first, InputSentinel last):m_elems(first, last) {
++        if (m_elems.empty()) {
++            Catch::throw_exception(GeneratorException("IteratorGenerator received no valid values"));
++        }
++    }
++
++    T const& get() const override {
++        return m_elems[m_current];
++    }
+ 
+-        auto const& generator = static_cast<Generators<UnderlyingType> const&>( *tracker.getGenerator() );
+-        return generator[tracker.getIndex()];
++    bool next() override {
++        ++m_current;
++        return m_current != m_elems.size();
+     }
++};
++
++template <typename InputIterator,
++          typename InputSentinel,
++          typename ResultType = typename std::iterator_traits<InputIterator>::value_type>
++GeneratorWrapper<ResultType> from_range(InputIterator from, InputSentinel to) {
++    return GeneratorWrapper<ResultType>(pf::make_unique<IteratorGenerator<ResultType>>(from, to));
++}
++
++template <typename Container,
++          typename ResultType = typename Container::value_type>
++GeneratorWrapper<ResultType> from_range(Container const& cnt) {
++    return GeneratorWrapper<ResultType>(pf::make_unique<IteratorGenerator<ResultType>>(cnt.begin(), cnt.end()));
++}
+ 
+ } // namespace Generators
+ } // namespace Catch
+ 
+-#define GENERATE( ... ) \
+-    Catch::Generators::generate( CATCH_INTERNAL_LINEINFO, []{ using namespace Catch::Generators; return makeGenerators( __VA_ARGS__ ); } )
+-
+-// end catch_generators.hpp
++// end catch_generators_specific.hpp
+ 
+ // These files are included here so the single_include script doesn't put them
+ // in the conditionally compiled sections
+@@ -3662,7 +4950,7 @@
+                     arcSafeRelease( m_substr );
+                 }
+ 
+-                bool match( NSString* arg ) const override {
++                bool match( NSString* str ) const override {
+                     return false;
+                 }
+ 
+@@ -3685,7 +4973,7 @@
+             struct Contains : StringHolder {
+                 Contains( NSString* substr ) : StringHolder( substr ){}
+ 
+-                bool match( NSString* str ) const {
++                bool match( NSString* str ) const override {
+                     return  (str != nil || m_substr == nil ) &&
+                             [str rangeOfString:m_substr].location != NSNotFound;
+                 }
+@@ -3761,7 +5049,8 @@
+ // end catch_objc.hpp
+ #endif
+ 
+-#ifdef CATCH_CONFIG_EXTERNAL_INTERFACES
++// Benchmarking needs the externally-facing parts of reporters to work
++#if defined(CATCH_CONFIG_EXTERNAL_INTERFACES) || defined(CATCH_CONFIG_ENABLE_BENCHMARKING)
+ // start catch_external_interfaces.h
+ 
+ // start catch_reporter_bases.hpp
+@@ -3803,7 +5092,7 @@
+         virtual bool matches( std::string const& str ) const;
+ 
+     private:
+-        std::string adjustCase( std::string const& str ) const;
++        std::string normaliseString( std::string const& str ) const;
+         CaseSensitive::Choice m_caseSensitivity;
+         WildcardPosition m_wildcard = NoWildcard;
+         std::string m_pattern;
+@@ -3817,36 +5106,40 @@
+ 
+ namespace Catch {
+ 
++    struct IConfig;
++
+     class TestSpec {
+-        struct Pattern {
++        class Pattern {
++        public:
++            explicit Pattern( std::string const& name );
+             virtual ~Pattern();
+             virtual bool matches( TestCaseInfo const& testCase ) const = 0;
++            std::string const& name() const;
++        private:
++            std::string const m_name;
+         };
+         using PatternPtr = std::shared_ptr<Pattern>;
+ 
+         class NamePattern : public Pattern {
+         public:
+-            NamePattern( std::string const& name );
+-            virtual ~NamePattern();
+-            virtual bool matches( TestCaseInfo const& testCase ) const override;
++            explicit NamePattern( std::string const& name, std::string const& filterString );
++            bool matches( TestCaseInfo const& testCase ) const override;
+         private:
+             WildcardPattern m_wildcardPattern;
+         };
+ 
+         class TagPattern : public Pattern {
+         public:
+-            TagPattern( std::string const& tag );
+-            virtual ~TagPattern();
+-            virtual bool matches( TestCaseInfo const& testCase ) const override;
++            explicit TagPattern( std::string const& tag, std::string const& filterString );
++            bool matches( TestCaseInfo const& testCase ) const override;
+         private:
+             std::string m_tag;
+         };
+ 
+         class ExcludedPattern : public Pattern {
+         public:
+-            ExcludedPattern( PatternPtr const& underlyingPattern );
+-            virtual ~ExcludedPattern();
+-            virtual bool matches( TestCaseInfo const& testCase ) const override;
++            explicit ExcludedPattern( PatternPtr const& underlyingPattern );
++            bool matches( TestCaseInfo const& testCase ) const override;
+         private:
+             PatternPtr m_underlyingPattern;
+         };
+@@ -3855,15 +5148,25 @@
+             std::vector<PatternPtr> m_patterns;
+ 
+             bool matches( TestCaseInfo const& testCase ) const;
++            std::string name() const;
+         };
+ 
+     public:
++        struct FilterMatch {
++            std::string name;
++            std::vector<TestCase const*> tests;
++        };
++        using Matches = std::vector<FilterMatch>;
++        using vectorStrings = std::vector<std::string>;
++
+         bool hasFilters() const;
+         bool matches( TestCaseInfo const& testCase ) const;
++        Matches matchesByFilter( std::vector<TestCase> const& testCases, IConfig const& config ) const;
++        const vectorStrings & getInvalidArgs() const;
+ 
+     private:
+         std::vector<Filter> m_filters;
+-
++        std::vector<std::string> m_invalidArgs;
+         friend class TestSpecParser;
+     };
+ }
+@@ -3898,9 +5201,13 @@
+     class TestSpecParser {
+         enum Mode{ None, Name, QuotedName, Tag, EscapedName };
+         Mode m_mode = None;
++        Mode lastMode = None;
+         bool m_exclusion = false;
+-        std::size_t m_start = std::string::npos, m_pos = 0;
++        std::size_t m_pos = 0;
++        std::size_t m_realPatternPos = 0;
+         std::string m_arg;
++        std::string m_substring;
++        std::string m_patternName;
+         std::vector<std::size_t> m_escapeChars;
+         TestSpec::Filter m_currentFilter;
+         TestSpec m_testSpec;
+@@ -3913,32 +5220,32 @@
+         TestSpec testSpec();
+ 
+     private:
+-        void visitChar( char c );
+-        void startNewMode( Mode mode, std::size_t start );
++        bool visitChar( char c );
++        void startNewMode( Mode mode );
++        bool processNoneChar( char c );
++        void processNameChar( char c );
++        bool processOtherChar( char c );
++        void endMode();
+         void escape();
+-        std::string subString() const;
++        bool isControlChar( char c ) const;
++        void saveLastMode();
++        void revertBackToLastMode();
++        void addFilter();
++        bool separate();
+ 
+-        template<typename T>
+-        void addPattern() {
+-            std::string token = subString();
+-            for( std::size_t i = 0; i < m_escapeChars.size(); ++i )
+-                token = token.substr( 0, m_escapeChars[i]-m_start-i ) + token.substr( m_escapeChars[i]-m_start-i+1 );
+-            m_escapeChars.clear();
+-            if( startsWith( token, "exclude:" ) ) {
+-                m_exclusion = true;
+-                token = token.substr( 8 );
+-            }
+-            if( !token.empty() ) {
+-                TestSpec::PatternPtr pattern = std::make_shared<T>( token );
+-                if( m_exclusion )
+-                    pattern = std::make_shared<TestSpec::ExcludedPattern>( pattern );
+-                m_currentFilter.m_patterns.push_back( pattern );
+-            }
+-            m_exclusion = false;
+-            m_mode = None;
++        // Handles common preprocessing of the pattern for name/tag patterns
++        std::string preprocessPattern();
++        // Adds the current pattern as a test name
++        void addNamePattern();
++        // Adds the current pattern as a tag
++        void addTagPattern();
++
++        inline void addCharToPattern(char c) {
++            m_substring += c;
++            m_patternName += c;
++            m_realPatternPos++;
+         }
+ 
+-        void addFilter();
+     };
+     TestSpec parseTestSpec( std::string const& arg );
+ 
+@@ -3949,79 +5256,6 @@
+ #endif
+ 
+ // end catch_test_spec_parser.h
+-// start catch_interfaces_config.h
+-
+-#include <iosfwd>
+-#include <string>
+-#include <vector>
+-#include <memory>
+-
+-namespace Catch {
+-
+-    enum class Verbosity {
+-        Quiet = 0,
+-        Normal,
+-        High
+-    };
+-
+-    struct WarnAbout { enum What {
+-        Nothing = 0x00,
+-        NoAssertions = 0x01,
+-        NoTests = 0x02
+-    }; };
+-
+-    struct ShowDurations { enum OrNot {
+-        DefaultForReporter,
+-        Always,
+-        Never
+-    }; };
+-    struct RunTests { enum InWhatOrder {
+-        InDeclarationOrder,
+-        InLexicographicalOrder,
+-        InRandomOrder
+-    }; };
+-    struct UseColour { enum YesOrNo {
+-        Auto,
+-        Yes,
+-        No
+-    }; };
+-    struct WaitForKeypress { enum When {
+-        Never,
+-        BeforeStart = 1,
+-        BeforeExit = 2,
+-        BeforeStartAndExit = BeforeStart | BeforeExit
+-    }; };
+-
+-    class TestSpec;
+-
+-    struct IConfig : NonCopyable {
+-
+-        virtual ~IConfig();
+-
+-        virtual bool allowThrows() const = 0;
+-        virtual std::ostream& stream() const = 0;
+-        virtual std::string name() const = 0;
+-        virtual bool includeSuccessfulResults() const = 0;
+-        virtual bool shouldDebugBreak() const = 0;
+-        virtual bool warnAboutMissingAssertions() const = 0;
+-        virtual bool warnAboutNoTests() const = 0;
+-        virtual int abortAfter() const = 0;
+-        virtual bool showInvisibles() const = 0;
+-        virtual ShowDurations::OrNot showDurations() const = 0;
+-        virtual TestSpec const& testSpec() const = 0;
+-        virtual bool hasTestFilters() const = 0;
+-        virtual RunTests::InWhatOrder runOrder() const = 0;
+-        virtual unsigned int rngSeed() const = 0;
+-        virtual int benchmarkResolutionMultiple() const = 0;
+-        virtual UseColour::YesOrNo useColour() const = 0;
+-        virtual std::vector<std::string> const& getSectionsToRun() const = 0;
+-        virtual Verbosity verbosity() const = 0;
+-    };
+-
+-    using IConfigPtr = std::shared_ptr<IConfig const>;
+-}
+-
+-// end catch_interfaces_config.h
+ // Libstdc++ doesn't like incomplete classes for unique_ptr
+ 
+ #include <memory>
+@@ -4052,11 +5286,17 @@
+ 
+         int abortAfter = -1;
+         unsigned int rngSeed = 0;
+-        int benchmarkResolutionMultiple = 100;
++
++        bool benchmarkNoAnalysis = false;
++        unsigned int benchmarkSamples = 100;
++        double benchmarkConfidenceInterval = 0.95;
++        unsigned int benchmarkResamples = 100000;
++        std::chrono::milliseconds::rep benchmarkWarmupTime = 100;
+ 
+         Verbosity verbosity = Verbosity::Normal;
+         WarnAbout::What warnings = WarnAbout::Nothing;
+         ShowDurations::OrNot showDurations = ShowDurations::DefaultForReporter;
++        double minDuration = -1;
+         RunTests::InWhatOrder runOrder = RunTests::InDeclarationOrder;
+         UseColour::YesOrNo useColour = UseColour::Auto;
+         WaitForKeypress::When waitForKeypress = WaitForKeypress::Never;
+@@ -4091,10 +5331,10 @@
+         std::string getProcessName() const;
+         std::string const& getReporterName() const;
+ 
+-        std::vector<std::string> const& getTestsOrTags() const;
++        std::vector<std::string> const& getTestsOrTags() const override;
+         std::vector<std::string> const& getSectionsToRun() const override;
+ 
+-        virtual TestSpec const& testSpec() const override;
++        TestSpec const& testSpec() const override;
+         bool hasTestFilters() const override;
+ 
+         bool showHelp() const;
+@@ -4107,14 +5347,19 @@
+         bool warnAboutMissingAssertions() const override;
+         bool warnAboutNoTests() const override;
+         ShowDurations::OrNot showDurations() const override;
++        double minDuration() const override;
+         RunTests::InWhatOrder runOrder() const override;
+         unsigned int rngSeed() const override;
+-        int benchmarkResolutionMultiple() const override;
+         UseColour::YesOrNo useColour() const override;
+         bool shouldDebugBreak() const override;
+         int abortAfter() const override;
+         bool showInvisibles() const override;
+         Verbosity verbosity() const override;
++        bool benchmarkNoAnalysis() const override;
++        int benchmarkSamples() const override;
++        double benchmarkConfidenceInterval() const override;
++        unsigned int benchmarkResamples() const override;
++        std::chrono::milliseconds benchmarkWarmupTime() const override;
+ 
+     private:
+ 
+@@ -4175,76 +5420,61 @@
+ } // end namespace Catch
+ 
+ // end catch_assertionresult.h
+-// start catch_option.hpp
++#if defined(CATCH_CONFIG_ENABLE_BENCHMARKING)
++// start catch_estimate.hpp
+ 
+-namespace Catch {
++ // Statistics estimates
+ 
+-    // An optional type
+-    template<typename T>
+-    class Option {
+-    public:
+-        Option() : nullableValue( nullptr ) {}
+-        Option( T const& _value )
+-        : nullableValue( new( storage ) T( _value ) )
+-        {}
+-        Option( Option const& _other )
+-        : nullableValue( _other ? new( storage ) T( *_other ) : nullptr )
+-        {}
+ 
+-        ~Option() {
+-            reset();
+-        }
++namespace Catch {
++    namespace Benchmark {
++        template <typename Duration>
++        struct Estimate {
++            Duration point;
++            Duration lower_bound;
++            Duration upper_bound;
++            double confidence_interval;
+ 
+-        Option& operator= ( Option const& _other ) {
+-            if( &_other != this ) {
+-                reset();
+-                if( _other )
+-                    nullableValue = new( storage ) T( *_other );
++            template <typename Duration2>
++            operator Estimate<Duration2>() const {
++                return { point, lower_bound, upper_bound, confidence_interval };
+             }
+-            return *this;
+-        }
+-        Option& operator = ( T const& _value ) {
+-            reset();
+-            nullableValue = new( storage ) T( _value );
+-            return *this;
+-        }
+-
+-        void reset() {
+-            if( nullableValue )
+-                nullableValue->~T();
+-            nullableValue = nullptr;
+-        }
++        };
++    } // namespace Benchmark
++} // namespace Catch
+ 
+-        T& operator*() { return *nullableValue; }
+-        T const& operator*() const { return *nullableValue; }
+-        T* operator->() { return nullableValue; }
+-        const T* operator->() const { return nullableValue; }
++// end catch_estimate.hpp
++// start catch_outlier_classification.hpp
+ 
+-        T valueOr( T const& defaultValue ) const {
+-            return nullableValue ? *nullableValue : defaultValue;
+-        }
++// Outlier information
+ 
+-        bool some() const { return nullableValue != nullptr; }
+-        bool none() const { return nullableValue == nullptr; }
++namespace Catch {
++    namespace Benchmark {
++        struct OutlierClassification {
++            int samples_seen = 0;
++            int low_severe = 0;     // more than 3 times IQR below Q1
++            int low_mild = 0;       // 1.5 to 3 times IQR below Q1
++            int high_mild = 0;      // 1.5 to 3 times IQR above Q3
++            int high_severe = 0;    // more than 3 times IQR above Q3
+ 
+-        bool operator !() const { return nullableValue == nullptr; }
+-        explicit operator bool() const {
+-            return some();
+-        }
++            int total() const {
++                return low_severe + low_mild + high_mild + high_severe;
++            }
++        };
++    } // namespace Benchmark
++} // namespace Catch
+ 
+-    private:
+-        T *nullableValue;
+-        alignas(alignof(T)) char storage[sizeof(T)];
+-    };
++// end catch_outlier_classification.hpp
+ 
+-} // end namespace Catch
++#include <iterator>
++#endif // CATCH_CONFIG_ENABLE_BENCHMARKING
+ 
+-// end catch_option.hpp
+ #include <string>
+ #include <iosfwd>
+ #include <map>
+ #include <set>
+ #include <memory>
++#include <algorithm>
+ 
+ namespace Catch {
+ 
+@@ -4301,8 +5531,8 @@
+ 
+         AssertionStats( AssertionStats const& )              = default;
+         AssertionStats( AssertionStats && )                  = default;
+-        AssertionStats& operator = ( AssertionStats const& ) = default;
+-        AssertionStats& operator = ( AssertionStats && )     = default;
++        AssertionStats& operator = ( AssertionStats const& ) = delete;
++        AssertionStats& operator = ( AssertionStats && )     = delete;
+         virtual ~AssertionStats();
+ 
+         AssertionResult assertionResult;
+@@ -4380,14 +5610,43 @@
+         bool aborting;
+     };
+ 
++#if defined(CATCH_CONFIG_ENABLE_BENCHMARKING)
+     struct BenchmarkInfo {
+         std::string name;
++        double estimatedDuration;
++        int iterations;
++        int samples;
++        unsigned int resamples;
++        double clockResolution;
++        double clockCost;
+     };
++
++    template <class Duration>
+     struct BenchmarkStats {
+         BenchmarkInfo info;
+-        std::size_t iterations;
+-        uint64_t elapsedTimeInNanoseconds;
++
++        std::vector<Duration> samples;
++        Benchmark::Estimate<Duration> mean;
++        Benchmark::Estimate<Duration> standardDeviation;
++        Benchmark::OutlierClassification outliers;
++        double outlierVariance;
++
++        template <typename Duration2>
++        operator BenchmarkStats<Duration2>() const {
++            std::vector<Duration2> samples2;
++            samples2.reserve(samples.size());
++            std::transform(samples.begin(), samples.end(), std::back_inserter(samples2), [](Duration d) { return Duration2(d); });
++            return {
++                info,
++                std::move(samples2),
++                mean,
++                standardDeviation,
++                outliers,
++                outlierVariance,
++            };
++        }
+     };
++#endif // CATCH_CONFIG_ENABLE_BENCHMARKING
+ 
+     struct IStreamingReporter {
+         virtual ~IStreamingReporter() = default;
+@@ -4400,23 +5659,26 @@
+ 
+         virtual void noMatchingTestCases( std::string const& spec ) = 0;
+ 
++        virtual void reportInvalidArguments(std::string const&) {}
++
+         virtual void testRunStarting( TestRunInfo const& testRunInfo ) = 0;
+         virtual void testGroupStarting( GroupInfo const& groupInfo ) = 0;
+ 
+         virtual void testCaseStarting( TestCaseInfo const& testInfo ) = 0;
+         virtual void sectionStarting( SectionInfo const& sectionInfo ) = 0;
+ 
+-        // *** experimental ***
++#if defined(CATCH_CONFIG_ENABLE_BENCHMARKING)
++        virtual void benchmarkPreparing( std::string const& ) {}
+         virtual void benchmarkStarting( BenchmarkInfo const& ) {}
++        virtual void benchmarkEnded( BenchmarkStats<> const& ) {}
++        virtual void benchmarkFailed( std::string const& ) {}
++#endif // CATCH_CONFIG_ENABLE_BENCHMARKING
+ 
+         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;
+ 
+-        // *** experimental ***
+-        virtual void benchmarkEnded( BenchmarkStats const& ) {}
+-
+         virtual void sectionEnded( SectionStats const& sectionStats ) = 0;
+         virtual void testCaseEnded( TestCaseStats const& testCaseStats ) = 0;
+         virtual void testGroupEnded( TestGroupStats const& testGroupStats ) = 0;
+@@ -4465,6 +5727,11 @@
+     // Returns double formatted as %.3f (format expected on output)
+     std::string getFormattedDuration( double duration );
+ 
++    //! Should the reporter show
++    bool shouldShowDuration( IConfig const& config, double duration );
++
++    std::string serializeFilters( std::vector<std::string> const& container );
++
+     template<typename DerivedT>
+     struct StreamingReporterBase : IStreamingReporter {
+ 
+@@ -4489,9 +5756,12 @@
+ 
+         void noMatchingTestCases(std::string const&) override {}
+ 
++        void reportInvalidArguments(std::string const&) override {}
++
+         void testRunStarting(TestRunInfo const& _testRunInfo) override {
+             currentTestRunInfo = _testRunInfo;
+         }
++
+         void testGroupStarting(GroupInfo const& _groupInfo) override {
+             currentGroupInfo = _groupInfo;
+         }
+@@ -4782,11 +6052,11 @@
+ 
+         class ReporterFactory : public IReporterFactory {
+ 
+-            virtual IStreamingReporterPtr create( ReporterConfig const& config ) const override {
++            IStreamingReporterPtr create( ReporterConfig const& config ) const override {
+                 return std::unique_ptr<T>( new T( config ) );
+             }
+ 
+-            virtual std::string getDescription() const override {
++            std::string getDescription() const override {
+                 return T::getDescription();
+             }
+         };
+@@ -4803,10 +6073,10 @@
+ 
+         class ListenerFactory : public IReporterFactory {
+ 
+-            virtual IStreamingReporterPtr create( ReporterConfig const& config ) const override {
++            IStreamingReporterPtr create( ReporterConfig const& config ) const override {
+                 return std::unique_ptr<T>( new T( config ) );
+             }
+-            virtual std::string getDescription() const override {
++            std::string getDescription() const override {
+                 return std::string();
+             }
+         };
+@@ -4822,14 +6092,16 @@
+ #if !defined(CATCH_CONFIG_DISABLE)
+ 
+ #define CATCH_REGISTER_REPORTER( name, reporterType ) \
++    CATCH_INTERNAL_START_WARNINGS_SUPPRESSION         \
+     CATCH_INTERNAL_SUPPRESS_GLOBALS_WARNINGS          \
+     namespace{ Catch::ReporterRegistrar<reporterType> catch_internal_RegistrarFor##reporterType( name ); } \
+-    CATCH_INTERNAL_UNSUPPRESS_GLOBALS_WARNINGS
++    CATCH_INTERNAL_STOP_WARNINGS_SUPPRESSION
+ 
+ #define CATCH_REGISTER_LISTENER( listenerType ) \
+-     CATCH_INTERNAL_SUPPRESS_GLOBALS_WARNINGS   \
+-     namespace{ Catch::ListenerRegistrar<listenerType> catch_internal_RegistrarFor##listenerType; } \
+-     CATCH_INTERNAL_SUPPRESS_GLOBALS_WARNINGS
++    CATCH_INTERNAL_START_WARNINGS_SUPPRESSION   \
++    CATCH_INTERNAL_SUPPRESS_GLOBALS_WARNINGS    \
++    namespace{ Catch::ListenerRegistrar<listenerType> catch_internal_RegistrarFor##listenerType; } \
++    CATCH_INTERNAL_STOP_WARNINGS_SUPPRESSION
+ #else // CATCH_CONFIG_DISABLE
+ 
+ #define CATCH_REGISTER_REPORTER(name, reporterType)
+@@ -4851,8 +6123,6 @@
+ 
+         static std::string getDescription();
+ 
+-        ReporterPreferences getPreferences() const override;
+-
+         void noMatchingTestCases(std::string const& spec) override;
+ 
+         void assertionStarting(AssertionInfo const&) override;
+@@ -4891,6 +6161,8 @@
+ 
+         void noMatchingTestCases(std::string const& spec) override;
+ 
++        void reportInvalidArguments(std::string const&arg) override;
++
+         void assertionStarting(AssertionInfo const&) override;
+ 
+         bool assertionEnded(AssertionStats const& _assertionStats) override;
+@@ -4898,13 +6170,17 @@
+         void sectionStarting(SectionInfo const& _sectionInfo) override;
+         void sectionEnded(SectionStats const& _sectionStats) override;
+ 
++#if defined(CATCH_CONFIG_ENABLE_BENCHMARKING)
++        void benchmarkPreparing(std::string const& name) override;
+         void benchmarkStarting(BenchmarkInfo const& info) override;
+-        void benchmarkEnded(BenchmarkStats const& stats) override;
++        void benchmarkEnded(BenchmarkStats<> const& stats) override;
++        void benchmarkFailed(std::string const& error) override;
++#endif // CATCH_CONFIG_ENABLE_BENCHMARKING
+ 
+         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:
+ 
+         void lazyPrint();
+@@ -4926,6 +6202,7 @@
+ 
+         void printTotalsDivider(Totals const& totals);
+         void printSummaryDivider();
++        void printTestFilters();
+ 
+     private:
+         bool m_headerPrinted = false;
+@@ -4945,6 +6222,14 @@
+ #include <vector>
+ 
+ namespace Catch {
++    enum class XmlFormatting {
++        None = 0x00,
++        Indent = 0x01,
++        Newline = 0x02,
++    };
++
++    XmlFormatting operator | (XmlFormatting lhs, XmlFormatting rhs);
++    XmlFormatting operator & (XmlFormatting lhs, XmlFormatting rhs);
+ 
+     class XmlEncode {
+     public:
+@@ -4966,14 +6251,14 @@
+ 
+         class ScopedElement {
+         public:
+-            ScopedElement( XmlWriter* writer );
++            ScopedElement( XmlWriter* writer, XmlFormatting fmt );
+ 
+             ScopedElement( ScopedElement&& other ) noexcept;
+             ScopedElement& operator=( ScopedElement&& other ) noexcept;
+ 
+             ~ScopedElement();
+ 
+-            ScopedElement& writeText( std::string const& text, bool indent = true );
++            ScopedElement& writeText( std::string const& text, XmlFormatting fmt = XmlFormatting::Newline | XmlFormatting::Indent );
+ 
+             template<typename T>
+             ScopedElement& writeAttribute( std::string const& name, T const& attribute ) {
+@@ -4983,6 +6268,7 @@
+ 
+         private:
+             mutable XmlWriter* m_writer = nullptr;
++            XmlFormatting m_fmt;
+         };
+ 
+         XmlWriter( std::ostream& os = Catch::cout() );
+@@ -4991,11 +6277,11 @@
+         XmlWriter( XmlWriter const& ) = delete;
+         XmlWriter& operator=( XmlWriter const& ) = delete;
+ 
+-        XmlWriter& startElement( std::string const& name );
++        XmlWriter& startElement( std::string const& name, XmlFormatting fmt = XmlFormatting::Newline | XmlFormatting::Indent);
+ 
+-        ScopedElement scopedElement( std::string const& name );
++        ScopedElement scopedElement( std::string const& name, XmlFormatting fmt = XmlFormatting::Newline | XmlFormatting::Indent);
+ 
+-        XmlWriter& endElement();
++        XmlWriter& endElement(XmlFormatting fmt = XmlFormatting::Newline | XmlFormatting::Indent);
+ 
+         XmlWriter& writeAttribute( std::string const& name, std::string const& attribute );
+ 
+@@ -5008,9 +6294,9 @@
+             return writeAttribute( name, rss.str() );
+         }
+ 
+-        XmlWriter& writeText( std::string const& text, bool indent = true );
++        XmlWriter& writeText( std::string const& text, XmlFormatting fmt = XmlFormatting::Newline | XmlFormatting::Indent);
+ 
+-        XmlWriter& writeComment( std::string const& text );
++        XmlWriter& writeComment(std::string const& text, XmlFormatting fmt = XmlFormatting::Newline | XmlFormatting::Indent);
+ 
+         void writeStylesheetRef( std::string const& url );
+ 
+@@ -5020,6 +6306,8 @@
+ 
+     private:
+ 
++        void applyFormatting(XmlFormatting fmt);
++
+         void writeDeclaration();
+ 
+         void newlineIfNecessary();
+@@ -5063,9 +6351,10 @@
+ 
+         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,
++                           bool testOkToFail );
+ 
+         void writeAssertions(SectionNode const& sectionNode);
+         void writeAssertion(AssertionStats const& stats);
+@@ -5120,6 +6409,13 @@
+ 
+         void testRunEnded(TestRunStats const& testRunStats) override;
+ 
++#if defined(CATCH_CONFIG_ENABLE_BENCHMARKING)
++        void benchmarkPreparing(std::string const& name) override;
++        void benchmarkStarting(BenchmarkInfo const&) override;
++        void benchmarkEnded(BenchmarkStats<> const&) override;
++        void benchmarkFailed(std::string const&) override;
++#endif // CATCH_CONFIG_ENABLE_BENCHMARKING
++
+     private:
+         Timer m_testCaseTimer;
+         XmlWriter m_xml;
+@@ -5133,6 +6429,1032 @@
+ // end catch_external_interfaces.h
+ #endif
+ 
++#if defined(CATCH_CONFIG_ENABLE_BENCHMARKING)
++// start catch_benchmarking_all.hpp
++
++// A proxy header that includes all of the benchmarking headers to allow
++// concise include of the benchmarking features. You should prefer the
++// individual includes in standard use.
++
++// start catch_benchmark.hpp
++
++ // Benchmark
++
++// start catch_chronometer.hpp
++
++// User-facing chronometer
++
++
++// start catch_clock.hpp
++
++// Clocks
++
++
++#include <chrono>
++#include <ratio>
++
++namespace Catch {
++    namespace Benchmark {
++        template <typename Clock>
++        using ClockDuration = typename Clock::duration;
++        template <typename Clock>
++        using FloatDuration = std::chrono::duration<double, typename Clock::period>;
++
++        template <typename Clock>
++        using TimePoint = typename Clock::time_point;
++
++        using default_clock = std::chrono::steady_clock;
++
++        template <typename Clock>
++        struct now {
++            TimePoint<Clock> operator()() const {
++                return Clock::now();
++            }
++        };
++
++        using fp_seconds = std::chrono::duration<double, std::ratio<1>>;
++    } // namespace Benchmark
++} // namespace Catch
++
++// end catch_clock.hpp
++// start catch_optimizer.hpp
++
++ // Hinting the optimizer
++
++
++#if defined(_MSC_VER)
++#   include <atomic> // atomic_thread_fence
++#endif
++
++namespace Catch {
++    namespace Benchmark {
++#if defined(__GNUC__) || defined(__clang__)
++        template <typename T>
++        inline void keep_memory(T* p) {
++            asm volatile("" : : "g"(p) : "memory");
++        }
++        inline void keep_memory() {
++            asm volatile("" : : : "memory");
++        }
++
++        namespace Detail {
++            inline void optimizer_barrier() { keep_memory(); }
++        } // namespace Detail
++#elif defined(_MSC_VER)
++
++#pragma optimize("", off)
++        template <typename T>
++        inline void keep_memory(T* p) {
++            // thanks @milleniumbug
++            *reinterpret_cast<char volatile*>(p) = *reinterpret_cast<char const volatile*>(p);
++        }
++        // TODO equivalent keep_memory()
++#pragma optimize("", on)
++
++        namespace Detail {
++            inline void optimizer_barrier() {
++                std::atomic_thread_fence(std::memory_order_seq_cst);
++            }
++        } // namespace Detail
++
++#endif
++
++        template <typename T>
++        inline void deoptimize_value(T&& x) {
++            keep_memory(&x);
++        }
++
++        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...)));
++        }
++
++        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...));
++        }
++    } // namespace Benchmark
++} // namespace Catch
++
++// end catch_optimizer.hpp
++// start catch_complete_invoke.hpp
++
++// Invoke with a special case for void
++
++
++#include <type_traits>
++#include <utility>
++
++namespace Catch {
++    namespace Benchmark {
++        namespace Detail {
++            template <typename T>
++            struct CompleteType { using type = T; };
++            template <>
++            struct CompleteType<void> { struct type {}; };
++
++            template <typename T>
++            using CompleteType_t = typename CompleteType<T>::type;
++
++            template <typename Result>
++            struct CompleteInvoker {
++                template <typename Fun, typename... Args>
++                static Result invoke(Fun&& fun, Args&&... args) {
++                    return std::forward<Fun>(fun)(std::forward<Args>(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)...);
++                    return {};
++                }
++            };
++
++            // 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)...);
++            }
++
++            const std::string benchmarkErrorMsg = "a benchmark failed to run successfully";
++        } // 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);
++            }
++        }
++    } // namespace Benchmark
++} // namespace Catch
++
++// end catch_complete_invoke.hpp
++namespace Catch {
++    namespace Benchmark {
++        namespace Detail {
++            struct ChronometerConcept {
++                virtual void start() = 0;
++                virtual void finish() = 0;
++                virtual ~ChronometerConcept() = default;
++            };
++            template <typename Clock>
++            struct ChronometerModel final : public ChronometerConcept {
++                void start() override { started = Clock::now(); }
++                void finish() override { finished = Clock::now(); }
++
++                ClockDuration<Clock> elapsed() const { return finished - started; }
++
++                TimePoint<Clock> started;
++                TimePoint<Clock> finished;
++            };
++        } // namespace Detail
++
++        struct Chronometer {
++        public:
++            template <typename Fun>
++            void measure(Fun&& fun) { measure(std::forward<Fun>(fun), is_callable<Fun(int)>()); }
++
++            int runs() const { return k; }
++
++            Chronometer(Detail::ChronometerConcept& meter, int k)
++                : impl(&meter)
++                , k(k) {}
++
++        private:
++            template <typename Fun>
++            void measure(Fun&& fun, std::false_type) {
++                measure([&fun](int) { return fun(); }, std::true_type());
++            }
++
++            template <typename Fun>
++            void measure(Fun&& fun, std::true_type) {
++                Detail::optimizer_barrier();
++                impl->start();
++                for (int i = 0; i < k; ++i) invoke_deoptimized(fun, i);
++                impl->finish();
++                Detail::optimizer_barrier();
++            }
++
++            Detail::ChronometerConcept* impl;
++            int k;
++        };
++    } // namespace Benchmark
++} // namespace Catch
++
++// end catch_chronometer.hpp
++// start catch_environment.hpp
++
++// Environment information
++
++
++namespace Catch {
++    namespace Benchmark {
++        template <typename Duration>
++        struct EnvironmentEstimate {
++            Duration mean;
++            OutlierClassification outliers;
++
++            template <typename Duration2>
++            operator EnvironmentEstimate<Duration2>() const {
++                return { mean, outliers };
++            }
++        };
++        template <typename Clock>
++        struct Environment {
++            using clock_type = Clock;
++            EnvironmentEstimate<FloatDuration<Clock>> clock_resolution;
++            EnvironmentEstimate<FloatDuration<Clock>> clock_cost;
++        };
++    } // namespace Benchmark
++} // namespace Catch
++
++// end catch_environment.hpp
++// start catch_execution_plan.hpp
++
++ // Execution plan
++
++
++// start catch_benchmark_function.hpp
++
++ // Dumb std::function implementation for consistent call overhead
++
++
++#include <cassert>
++#include <type_traits>
++#include <utility>
++#include <memory>
++
++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>> {};
++
++            /// 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
++            /// can account for it in the measurement.
++            /// Implementations of std::function with optimizations that aren't always applicable, like
++            /// small buffer optimizations, are not uncommon.
++            /// This is effectively an implementation of std::function without any such optimizations;
++            /// it may be slow, but it is consistently slow.
++            struct BenchmarkFunction {
++            private:
++                struct callable {
++                    virtual void call(Chronometer meter) const = 0;
++                    virtual callable* clone() const = 0;
++                    virtual ~callable() = default;
++                };
++                template <typename Fun>
++                struct model : public callable {
++                    model(Fun&& fun) : fun(std::move(fun)) {}
++                    model(Fun const& fun) : fun(fun) {}
++
++                    model<Fun>* clone() const override { return new model<Fun>(*this); }
++
++                    void call(Chronometer meter) const override {
++                        call(meter, is_callable<Fun(Chronometer)>());
++                    }
++                    void call(Chronometer meter, std::true_type) const {
++                        fun(meter);
++                    }
++                    void call(Chronometer meter, std::false_type) const {
++                        meter.measure(fun);
++                    }
++
++                    Fun fun;
++                };
++
++                struct do_nothing { void operator()() const {} };
++
++                template <typename T>
++                BenchmarkFunction(model<T>* c) : f(c) {}
++
++            public:
++                BenchmarkFunction()
++                    : f(new model<do_nothing>{ {} }) {}
++
++                template <typename Fun,
++                    typename std::enable_if<!is_related<Fun, BenchmarkFunction>::value, int>::type = 0>
++                    BenchmarkFunction(Fun&& fun)
++                    : f(new model<typename std::decay<Fun>::type>(std::forward<Fun>(fun))) {}
++
++                BenchmarkFunction(BenchmarkFunction&& that)
++                    : f(std::move(that.f)) {}
++
++                BenchmarkFunction(BenchmarkFunction const& that)
++                    : f(that.f->clone()) {}
++
++                BenchmarkFunction& operator=(BenchmarkFunction&& that) {
++                    f = std::move(that.f);
++                    return *this;
++                }
++
++                BenchmarkFunction& operator=(BenchmarkFunction const& that) {
++                    f.reset(that.f->clone());
++                    return *this;
++                }
++
++                void operator()(Chronometer meter) const { f->call(meter); }
++
++            private:
++                std::unique_ptr<callable> f;
++            };
++        } // namespace Detail
++    } // namespace Benchmark
++} // namespace Catch
++
++// end catch_benchmark_function.hpp
++// start catch_repeat.hpp
++
++// repeat algorithm
++
++
++#include <type_traits>
++#include <utility>
++
++namespace Catch {
++    namespace Benchmark {
++        namespace Detail {
++            template <typename Fun>
++            struct repeater {
++                void operator()(int k) const {
++                    for (int i = 0; i < k; ++i) {
++                        fun();
++                    }
++                }
++                Fun fun;
++            };
++            template <typename Fun>
++            repeater<typename std::decay<Fun>::type> repeat(Fun&& fun) {
++                return { std::forward<Fun>(fun) };
++            }
++        } // namespace Detail
++    } // namespace Benchmark
++} // namespace Catch
++
++// end catch_repeat.hpp
++// start catch_run_for_at_least.hpp
++
++// Run a function for a minimum amount of time
++
++
++// start catch_measure.hpp
++
++// Measure
++
++
++// start catch_timing.hpp
++
++// Timing
++
++
++#include <tuple>
++#include <type_traits>
++
++namespace Catch {
++    namespace Benchmark {
++        template <typename Duration, typename Result>
++        struct Timing {
++            Duration elapsed;
++            Result result;
++            int iterations;
++        };
++        template <typename Clock, typename Func, typename... Args>
++        using TimingOf = Timing<ClockDuration<Clock>, Detail::CompleteType_t<FunctionReturnType<Func, Args...>>>;
++    } // namespace Benchmark
++} // namespace Catch
++
++// end catch_timing.hpp
++#include <utility>
++
++namespace Catch {
++    namespace Benchmark {
++        namespace Detail {
++            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 end = Clock::now();
++                auto delta = end - start;
++                return { delta, std::forward<decltype(r)>(r), 1 };
++            }
++        } // namespace Detail
++    } // namespace Benchmark
++} // namespace Catch
++
++// end catch_measure.hpp
++#include <utility>
++#include <type_traits>
++
++namespace Catch {
++    namespace Benchmark {
++        namespace Detail {
++            template <typename Clock, typename Fun>
++            TimingOf<Clock, Fun, int> measure_one(Fun&& fun, int iters, std::false_type) {
++                return Detail::measure<Clock>(fun, iters);
++            }
++            template <typename Clock, typename Fun>
++            TimingOf<Clock, Fun, Chronometer> measure_one(Fun&& fun, int iters, std::true_type) {
++                Detail::ChronometerModel<Clock> meter;
++                auto&& result = Detail::complete_invoke(fun, Chronometer(meter, iters));
++
++                return { meter.elapsed(), std::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;
++
++            struct optimized_away_error : std::exception {
++                const char* what() const noexcept override {
++                    return "could not measure benchmark, maybe it was optimized away";
++                }
++            };
++
++            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;
++                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 };
++                    }
++                    iters *= 2;
++                }
++                Catch::throw_exception(optimized_away_error{});
++            }
++        } // namespace Detail
++    } // namespace Benchmark
++} // namespace Catch
++
++// end catch_run_for_at_least.hpp
++#include <algorithm>
++#include <iterator>
++
++namespace Catch {
++    namespace Benchmark {
++        template <typename Duration>
++        struct ExecutionPlan {
++            int iterations_per_sample;
++            Duration estimated_duration;
++            Detail::BenchmarkFunction benchmark;
++            Duration warmup_time;
++            int warmup_iterations;
++
++            template <typename Duration2>
++            operator ExecutionPlan<Duration2>() const {
++                return { iterations_per_sample, estimated_duration, benchmark, warmup_time, warmup_iterations };
++            }
++
++            template <typename Clock>
++            std::vector<FloatDuration<Clock>> run(const IConfig &cfg, Environment<FloatDuration<Clock>> env) const {
++                // warmup a bit
++                Detail::run_for_at_least<Clock>(std::chrono::duration_cast<ClockDuration<Clock>>(warmup_time), warmup_iterations, Detail::repeat(now<Clock>{}));
++
++                std::vector<FloatDuration<Clock>> times;
++                times.reserve(cfg.benchmarkSamples());
++                std::generate_n(std::back_inserter(times), cfg.benchmarkSamples(), [this, env] {
++                    Detail::ChronometerModel<Clock> model;
++                    this->benchmark(Chronometer(model, iterations_per_sample));
++                    auto sample_time = model.elapsed() - env.clock_cost.mean;
++                    if (sample_time < FloatDuration<Clock>::zero()) sample_time = FloatDuration<Clock>::zero();
++                    return sample_time / iterations_per_sample;
++                });
++                return times;
++            }
++        };
++    } // namespace Benchmark
++} // namespace Catch
++
++// end catch_execution_plan.hpp
++// start catch_estimate_clock.hpp
++
++ // Environment measurement
++
++
++// start catch_stats.hpp
++
++// Statistical analysis tools
++
++
++#include <algorithm>
++#include <functional>
++#include <vector>
++#include <iterator>
++#include <numeric>
++#include <tuple>
++#include <cmath>
++#include <utility>
++#include <cstddef>
++#include <random>
++
++namespace Catch {
++    namespace Benchmark {
++        namespace Detail {
++            using sample = std::vector<double>;
++
++            double weighted_average_quantile(int k, int q, std::vector<double>::iterator first, std::vector<double>::iterator last);
++
++            template <typename Iterator>
++            OutlierClassification classify_outliers(Iterator first, Iterator last) {
++                std::vector<double> copy(first, last);
++
++                auto q1 = weighted_average_quantile(1, 4, copy.begin(), copy.end());
++                auto q3 = weighted_average_quantile(3, 4, copy.begin(), copy.end());
++                auto iqr = q3 - q1;
++                auto los = q1 - (iqr * 3.);
++                auto lom = q1 - (iqr * 1.5);
++                auto him = q3 + (iqr * 1.5);
++                auto his = q3 + (iqr * 3.);
++
++                OutlierClassification o;
++                for (; first != last; ++first) {
++                    auto&& t = *first;
++                    if (t < los) ++o.low_severe;
++                    else if (t < lom) ++o.low_mild;
++                    else if (t > his) ++o.high_severe;
++                    else if (t > him) ++o.high_mild;
++                    ++o.samples_seen;
++                }
++                return o;
++            }
++
++            template <typename Iterator>
++            double mean(Iterator first, Iterator last) {
++                auto count = last - first;
++                double sum = std::accumulate(first, last, 0.);
++                return sum / count;
++            }
++
++            template <typename URng, typename Iterator, typename Estimator>
++            sample resample(URng& rng, int resamples, Iterator first, Iterator last, Estimator& estimator) {
++                auto n = last - first;
++                std::uniform_int_distribution<decltype(n)> dist(0, n - 1);
++
++                sample out;
++                out.reserve(resamples);
++                std::generate_n(std::back_inserter(out), resamples, [n, first, &estimator, &dist, &rng] {
++                    std::vector<double> resampled;
++                    resampled.reserve(n);
++                    std::generate_n(std::back_inserter(resampled), n, [first, &dist, &rng] { return first[dist(rng)]; });
++                    return estimator(resampled.begin(), resampled.end());
++                });
++                std::sort(out.begin(), out.end());
++                return out;
++            }
++
++            template <typename Estimator, typename Iterator>
++            sample jackknife(Estimator&& estimator, Iterator first, Iterator last) {
++                auto n = last - first;
++                auto second = std::next(first);
++                sample results;
++                results.reserve(n);
++
++                for (auto it = first; it != last; ++it) {
++                    std::iter_swap(it, first);
++                    results.push_back(estimator(second, last));
++                }
++
++                return results;
++            }
++
++            inline double normal_cdf(double x) {
++                return std::erfc(-x / std::sqrt(2.0)) / 2.0;
++            }
++
++            double erfc_inv(double x);
++
++            double normal_quantile(double p);
++
++            template <typename Iterator, typename Estimator>
++            Estimate<double> bootstrap(double confidence_level, Iterator first, Iterator last, sample const& resample, Estimator&& estimator) {
++                auto n_samples = last - first;
++
++                double point = estimator(first, last);
++                // Degenerate case with a single sample
++                if (n_samples == 1) return { point, point, point, confidence_level };
++
++                sample jack = jackknife(estimator, first, last);
++                double jack_mean = mean(jack.begin(), jack.end());
++                double sum_squares, sum_cubes;
++                std::tie(sum_squares, sum_cubes) = std::accumulate(jack.begin(), jack.end(), std::make_pair(0., 0.), [jack_mean](std::pair<double, double> sqcb, double x) -> std::pair<double, double> {
++                    auto d = jack_mean - x;
++                    auto d2 = d * d;
++                    auto d3 = d2 * d;
++                    return { sqcb.first + d2, sqcb.second + d3 };
++                });
++
++                double accel = sum_cubes / (6 * std::pow(sum_squares, 1.5));
++                int n = static_cast<int>(resample.size());
++                double prob_n = std::count_if(resample.begin(), resample.end(), [point](double x) { return x < point; }) / (double)n;
++                // degenerate case with uniform samples
++                if (prob_n == 0) return { point, point, point, confidence_level };
++
++                double bias = normal_quantile(prob_n);
++                double z1 = normal_quantile((1. - confidence_level) / 2.);
++
++                auto cumn = [n](double x) -> int {
++                    return std::lround(normal_cdf(x) * n); };
++                auto a = [bias, accel](double b) { return bias + b / (1. - accel * b); };
++                double b1 = bias + z1;
++                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);
++
++                return { point, resample[lo], resample[hi], confidence_level };
++            }
++
++            double outlier_variance(Estimate<double> mean, Estimate<double> stddev, int n);
++
++            struct bootstrap_analysis {
++                Estimate<double> mean;
++                Estimate<double> standard_deviation;
++                double outlier_variance;
++            };
++
++            bootstrap_analysis analyse_samples(double confidence_level, int n_resamples, std::vector<double>::iterator first, std::vector<double>::iterator last);
++        } // namespace Detail
++    } // namespace Benchmark
++} // namespace Catch
++
++// end catch_stats.hpp
++#include <algorithm>
++#include <iterator>
++#include <tuple>
++#include <vector>
++#include <cmath>
++
++namespace Catch {
++    namespace Benchmark {
++        namespace Detail {
++            template <typename Clock>
++            std::vector<double> resolution(int k) {
++                std::vector<TimePoint<Clock>> times;
++                times.reserve(k + 1);
++                std::generate_n(std::back_inserter(times), k + 1, now<Clock>{});
++
++                std::vector<double> deltas;
++                deltas.reserve(k);
++                std::transform(std::next(times.begin()), times.end(), times.begin(),
++                    std::back_inserter(deltas),
++                    [](TimePoint<Clock> a, TimePoint<Clock> b) { return static_cast<double>((a - b).count()); });
++
++                return deltas;
++            }
++
++            const auto warmup_iterations = 10000;
++            const auto warmup_time = std::chrono::milliseconds(100);
++            const auto minimum_ticks = 1000;
++            const auto warmup_seed = 10000;
++            const auto clock_resolution_estimation_time = std::chrono::milliseconds(500);
++            const auto clock_cost_estimation_time_limit = std::chrono::seconds(1);
++            const auto clock_cost_estimation_tick_limit = 100000;
++            const auto clock_cost_estimation_time = std::chrono::milliseconds(10);
++            const auto clock_cost_estimation_iterations = 10000;
++
++            template <typename Clock>
++            int warmup() {
++                return run_for_at_least<Clock>(std::chrono::duration_cast<ClockDuration<Clock>>(warmup_time), warmup_seed, &resolution<Clock>)
++                    .iterations;
++            }
++            template <typename Clock>
++            EnvironmentEstimate<FloatDuration<Clock>> estimate_clock_resolution(int iterations) {
++                auto r = run_for_at_least<Clock>(std::chrono::duration_cast<ClockDuration<Clock>>(clock_resolution_estimation_time), iterations, &resolution<Clock>)
++                    .result;
++                return {
++                    FloatDuration<Clock>(mean(r.begin(), r.end())),
++                    classify_outliers(r.begin(), r.end()),
++                };
++            }
++            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_clock = [](int k) {
++                    return Detail::measure<Clock>([k] {
++                        for (int i = 0; i < k; ++i) {
++                            volatile auto ignored = Clock::now();
++                            (void)ignored;
++                        }
++                    }).elapsed;
++                };
++                time_clock(1);
++                int iters = clock_cost_estimation_iterations;
++                auto&& r = run_for_at_least<Clock>(std::chrono::duration_cast<ClockDuration<Clock>>(clock_cost_estimation_time), iters, time_clock);
++                std::vector<double> times;
++                int nsamples = static_cast<int>(std::ceil(time_limit / r.elapsed));
++                times.reserve(nsamples);
++                std::generate_n(std::back_inserter(times), nsamples, [time_clock, &r] {
++                    return static_cast<double>((time_clock(r.iterations) / r.iterations).count());
++                });
++                return {
++                    FloatDuration<Clock>(mean(times.begin(), times.end())),
++                    classify_outliers(times.begin(), times.end()),
++                };
++            }
++
++            template <typename Clock>
++            Environment<FloatDuration<Clock>> measure_environment() {
++                static Environment<FloatDuration<Clock>>* env = nullptr;
++                if (env) {
++                    return *env;
++                }
++
++                auto iters = Detail::warmup<Clock>();
++                auto resolution = Detail::estimate_clock_resolution<Clock>(iters);
++                auto cost = Detail::estimate_clock_cost<Clock>(resolution.mean);
++
++                env = new Environment<FloatDuration<Clock>>{ resolution, cost };
++                return *env;
++            }
++        } // namespace Detail
++    } // namespace Benchmark
++} // namespace Catch
++
++// end catch_estimate_clock.hpp
++// start catch_analyse.hpp
++
++ // Run and analyse one benchmark
++
++
++// start catch_sample_analysis.hpp
++
++// Benchmark results
++
++
++#include <algorithm>
++#include <vector>
++#include <string>
++#include <iterator>
++
++namespace Catch {
++    namespace Benchmark {
++        template <typename Duration>
++        struct SampleAnalysis {
++            std::vector<Duration> samples;
++            Estimate<Duration> mean;
++            Estimate<Duration> standard_deviation;
++            OutlierClassification outliers;
++            double outlier_variance;
++
++            template <typename Duration2>
++            operator SampleAnalysis<Duration2>() const {
++                std::vector<Duration2> samples2;
++                samples2.reserve(samples.size());
++                std::transform(samples.begin(), samples.end(), std::back_inserter(samples2), [](Duration d) { return Duration2(d); });
++                return {
++                    std::move(samples2),
++                    mean,
++                    standard_deviation,
++                    outliers,
++                    outlier_variance,
++                };
++            }
++        };
++    } // namespace Benchmark
++} // namespace Catch
++
++// end catch_sample_analysis.hpp
++#include <algorithm>
++#include <iterator>
++#include <vector>
++
++namespace Catch {
++    namespace Benchmark {
++        namespace Detail {
++            template <typename Duration, typename Iterator>
++            SampleAnalysis<Duration> analyse(const IConfig &cfg, Environment<Duration>, Iterator first, Iterator last) {
++                if (!cfg.benchmarkNoAnalysis()) {
++                    std::vector<double> samples;
++                    samples.reserve(last - first);
++                    std::transform(first, last, std::back_inserter(samples), [](Duration d) { return d.count(); });
++
++                    auto analysis = Catch::Benchmark::Detail::analyse_samples(cfg.benchmarkConfidenceInterval(), cfg.benchmarkResamples(), samples.begin(), samples.end());
++                    auto outliers = Catch::Benchmark::Detail::classify_outliers(samples.begin(), samples.end());
++
++                    auto wrap_estimate = [](Estimate<double> e) {
++                        return Estimate<Duration> {
++                            Duration(e.point),
++                                Duration(e.lower_bound),
++                                Duration(e.upper_bound),
++                                e.confidence_interval,
++                        };
++                    };
++                    std::vector<Duration> samples2;
++                    samples2.reserve(samples.size());
++                    std::transform(samples.begin(), samples.end(), std::back_inserter(samples2), [](double d) { return Duration(d); });
++                    return {
++                        std::move(samples2),
++                        wrap_estimate(analysis.mean),
++                        wrap_estimate(analysis.standard_deviation),
++                        outliers,
++                        analysis.outlier_variance,
++                    };
++                } else {
++                    std::vector<Duration> samples;
++                    samples.reserve(last - first);
++
++                    Duration mean = Duration(0);
++                    int i = 0;
++                    for (auto it = first; it < last; ++it, ++i) {
++                        samples.push_back(Duration(*it));
++                        mean += Duration(*it);
++                    }
++                    mean /= i;
++
++                    return {
++                        std::move(samples),
++                        Estimate<Duration>{mean, mean, mean, 0.0},
++                        Estimate<Duration>{Duration(0), Duration(0), Duration(0), 0.0},
++                        OutlierClassification{},
++                        0.0
++                    };
++                }
++            }
++        } // namespace Detail
++    } // namespace Benchmark
++} // namespace Catch
++
++// end catch_analyse.hpp
++#include <algorithm>
++#include <functional>
++#include <string>
++#include <vector>
++#include <cmath>
++
++namespace Catch {
++    namespace Benchmark {
++        struct Benchmark {
++            Benchmark(std::string &&name)
++                : name(std::move(name)) {}
++
++            template <class FUN>
++            Benchmark(std::string &&name, FUN &&func)
++                : fun(std::move(func)), name(std::move(name)) {}
++
++            template <typename Clock>
++            ExecutionPlan<FloatDuration<Clock>> prepare(const IConfig &cfg, Environment<FloatDuration<Clock>> env) const {
++                auto min_time = env.clock_resolution.mean * Detail::minimum_ticks;
++                auto run_time = std::max(min_time, std::chrono::duration_cast<decltype(min_time)>(cfg.benchmarkWarmupTime()));
++                auto&& test = Detail::run_for_at_least<Clock>(std::chrono::duration_cast<ClockDuration<Clock>>(run_time), 1, fun);
++                int new_iters = static_cast<int>(std::ceil(min_time * test.iterations / test.elapsed));
++                return { new_iters, test.elapsed / test.iterations * new_iters * cfg.benchmarkSamples(), fun, std::chrono::duration_cast<FloatDuration<Clock>>(cfg.benchmarkWarmupTime()), Detail::warmup_iterations };
++            }
++
++            template <typename Clock = default_clock>
++            void run() {
++                IConfigPtr cfg = getCurrentContext().getConfig();
++
++                auto env = Detail::measure_environment<Clock>();
++
++                getResultCapture().benchmarkPreparing(name);
++                CATCH_TRY{
++                    auto plan = user_code([&] {
++                        return prepare<Clock>(*cfg, env);
++                    });
++
++                    BenchmarkInfo info {
++                        name,
++                        plan.estimated_duration.count(),
++                        plan.iterations_per_sample,
++                        cfg->benchmarkSamples(),
++                        cfg->benchmarkResamples(),
++                        env.clock_resolution.mean.count(),
++                        env.clock_cost.mean.count()
++                    };
++
++                    getResultCapture().benchmarkStarting(info);
++
++                    auto samples = user_code([&] {
++                        return plan.template run<Clock>(*cfg, env);
++                    });
++
++                    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_ALL{
++                    if (translateActiveException() != Detail::benchmarkErrorMsg) // benchmark errors have been reported, otherwise rethrow.
++                        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>
++                Benchmark & operator=(Fun func) {
++                fun = Detail::BenchmarkFunction(func);
++                run();
++                return *this;
++            }
++
++            explicit operator bool() {
++                return true;
++            }
++
++        private:
++            Detail::BenchmarkFunction fun;
++            std::string name;
++        };
++    }
++} // namespace Catch
++
++#define INTERNAL_CATCH_GET_1_ARG(arg1, arg2, ...) arg1
++#define INTERNAL_CATCH_GET_2_ARG(arg1, arg2, ...) arg2
++
++#define INTERNAL_CATCH_BENCHMARK(BenchmarkName, name, benchmarkIndex)\
++    if( Catch::Benchmark::Benchmark BenchmarkName{name} ) \
++        BenchmarkName = [&](int benchmarkIndex)
++
++#define INTERNAL_CATCH_BENCHMARK_ADVANCED(BenchmarkName, name)\
++    if( Catch::Benchmark::Benchmark BenchmarkName{name} ) \
++        BenchmarkName = [&]
++
++// end catch_benchmark.hpp
++// start catch_constructor.hpp
++
++// Constructor and destructor helpers
++
++
++#include <type_traits>
++
++namespace Catch {
++    namespace Benchmark {
++        namespace Detail {
++            template <typename T, bool Destruct>
++            struct ObjectStorage
++            {
++                using TStorage = typename std::aligned_storage<sizeof(T), std::alignment_of<T>::value>::type;
++
++                ObjectStorage() : data() {}
++
++                ObjectStorage(const ObjectStorage& other)
++                {
++                    new(&data) T(other.stored_object());
++                }
++
++                ObjectStorage(ObjectStorage&& other)
++                {
++                    new(&data) T(std::move(other.stored_object()));
++                }
++
++                ~ObjectStorage() { destruct_on_exit<T>(); }
++
++                template <typename... Args>
++                void construct(Args&&... args)
++                {
++                    new (&data) T(std::forward<Args>(args)...);
++                }
++
++                template <bool AllowManualDestruction = !Destruct>
++                typename std::enable_if<AllowManualDestruction>::type destruct()
++                {
++                    stored_object().~T();
++                }
++
++            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>(); }
++                // Otherwise, don't
++                template <typename U>
++                void destruct_on_exit(typename std::enable_if<!Destruct, U>::type* = 0) { }
++
++                T& stored_object() {
++                    return *static_cast<T*>(static_cast<void*>(&data));
++                }
++
++                T const& stored_object() const {
++                    return *static_cast<T*>(static_cast<void*>(&data));
++                }
++
++                TStorage data;
++            };
++        }
++
++        template <typename T>
++        using storage_for = Detail::ObjectStorage<T, true>;
++
++        template <typename T>
++        using destructable_object = Detail::ObjectStorage<T, false>;
++    }
++}
++
++// end catch_constructor.hpp
++// end catch_benchmarking_all.hpp
++#endif
++
+ #endif // ! CATCH_CONFIG_IMPL_ONLY
+ 
+ #ifdef CATCH_IMPL
+@@ -5158,23 +7480,37 @@
+         SourceLineInfo location;
+ 
+         NameAndLocation( std::string const& _name, SourceLineInfo const& _location );
++        friend bool operator==(NameAndLocation const& lhs, NameAndLocation const& rhs) {
++            return lhs.name == rhs.name
++                && lhs.location == rhs.location;
++        }
+     };
+ 
+-    struct ITracker;
++    class ITracker;
+ 
+     using ITrackerPtr = std::shared_ptr<ITracker>;
+ 
+-    struct ITracker {
+-        virtual ~ITracker();
++    class  ITracker {
++        NameAndLocation m_nameAndLocation;
++
++    public:
++        ITracker(NameAndLocation const& nameAndLoc) :
++            m_nameAndLocation(nameAndLoc)
++        {}
+ 
+         // static queries
+-        virtual NameAndLocation const& nameAndLocation() const = 0;
++        NameAndLocation const& nameAndLocation() const {
++            return m_nameAndLocation;
++        }
++
++        virtual ~ITracker();
+ 
+         // 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 hasChildren() const = 0;
++        virtual bool hasStarted() const = 0;
+ 
+         virtual ITracker& parent() = 0;
+ 
+@@ -5189,7 +7525,7 @@
+ 
+         // Debug/ checking
+         virtual bool isSectionTracker() const = 0;
+-        virtual bool isIndexTracker() const = 0;
++        virtual bool isGeneratorTracker() const = 0;
+     };
+ 
+     class TrackerContext {
+@@ -5206,8 +7542,6 @@
+ 
+     public:
+ 
+-        static TrackerContext& instance();
+-
+         ITracker& startRun();
+         void endRun();
+ 
+@@ -5231,7 +7565,6 @@
+         };
+ 
+         using Children = std::vector<ITrackerPtr>;
+-        NameAndLocation m_nameAndLocation;
+         TrackerContext& m_ctx;
+         ITracker* m_parent;
+         Children m_children;
+@@ -5240,11 +7573,13 @@
+     public:
+         TrackerBase( NameAndLocation const& nameAndLocation, TrackerContext& ctx, ITracker* parent );
+ 
+-        NameAndLocation const& nameAndLocation() const override;
+         bool isComplete() const override;
+         bool isSuccessfullyCompleted() const override;
+         bool isOpen() const override;
+         bool hasChildren() const override;
++        bool hasStarted() const override {
++            return m_runState != NotStarted;
++        }
+ 
+         void addChild( ITrackerPtr const& child ) override;
+ 
+@@ -5254,7 +7589,7 @@
+         void openChild() override;
+ 
+         bool isSectionTracker() const override;
+-        bool isIndexTracker() const override;
++        bool isGeneratorTracker() const override;
+ 
+         void open();
+ 
+@@ -5269,33 +7604,24 @@
+ 
+     class SectionTracker : public TrackerBase {
+         std::vector<std::string> m_filters;
++        std::string m_trimmed_name;
+     public:
+         SectionTracker( NameAndLocation const& nameAndLocation, TrackerContext& ctx, ITracker* parent );
+ 
+         bool isSectionTracker() const override;
+ 
++        bool isComplete() const override;
++
+         static SectionTracker& acquire( TrackerContext& ctx, NameAndLocation const& nameAndLocation );
+ 
+         void tryOpen();
+ 
+         void addInitialFilters( std::vector<std::string> const& filters );
+         void addNextFilters( std::vector<std::string> const& filters );
+-    };
+-
+-    class IndexTracker : public TrackerBase {
+-        int m_size;
+-        int m_index = -1;
+-    public:
+-        IndexTracker( NameAndLocation const& nameAndLocation, TrackerContext& ctx, ITracker* parent, int size );
+-
+-        bool isIndexTracker() const override;
+-        void close() override;
+-
+-        static IndexTracker& acquire( TrackerContext& ctx, NameAndLocation const& nameAndLocation, int size );
+-
+-        int index() const;
+-
+-        void moveNext();
++        //! Returns filters active in this tracker
++        std::vector<std::string> const& getFilters() const;
++        //! Returns whitespace-trimmed name of the tracked section
++        std::string const& trimmedName() const;
+     };
+ 
+ } // namespace TestCaseTracking
+@@ -5303,7 +7629,6 @@
+ using TestCaseTracking::ITracker;
+ using TestCaseTracking::TrackerContext;
+ using TestCaseTracking::SectionTracker;
+-using TestCaseTracking::IndexTracker;
+ 
+ } // namespace Catch
+ 
+@@ -5321,6 +7646,217 @@
+ }
+ // end catch_leak_detector.h
+ // Cpp files will be included in the single-header file here
++// start catch_stats.cpp
++
++// Statistical analysis tools
++
++#if defined(CATCH_CONFIG_ENABLE_BENCHMARKING)
++
++#include <cassert>
++#include <random>
++
++#if defined(CATCH_CONFIG_USE_ASYNC)
++#include <future>
++#endif
++
++namespace {
++    double erf_inv(double x) {
++        // Code accompanying the article "Approximating the erfinv function" in GPU Computing Gems, Volume 2
++        double w, p;
++
++        w = -log((1.0 - x) * (1.0 + x));
++
++        if (w < 6.250000) {
++            w = w - 3.125000;
++            p = -3.6444120640178196996e-21;
++            p = -1.685059138182016589e-19 + p * w;
++            p = 1.2858480715256400167e-18 + p * w;
++            p = 1.115787767802518096e-17 + p * w;
++            p = -1.333171662854620906e-16 + p * w;
++            p = 2.0972767875968561637e-17 + p * w;
++            p = 6.6376381343583238325e-15 + p * w;
++            p = -4.0545662729752068639e-14 + p * w;
++            p = -8.1519341976054721522e-14 + p * w;
++            p = 2.6335093153082322977e-12 + p * w;
++            p = -1.2975133253453532498e-11 + p * w;
++            p = -5.4154120542946279317e-11 + p * w;
++            p = 1.051212273321532285e-09 + p * w;
++            p = -4.1126339803469836976e-09 + p * w;
++            p = -2.9070369957882005086e-08 + p * w;
++            p = 4.2347877827932403518e-07 + p * w;
++            p = -1.3654692000834678645e-06 + p * w;
++            p = -1.3882523362786468719e-05 + p * w;
++            p = 0.0001867342080340571352 + p * w;
++            p = -0.00074070253416626697512 + p * w;
++            p = -0.0060336708714301490533 + p * w;
++            p = 0.24015818242558961693 + p * w;
++            p = 1.6536545626831027356 + p * w;
++        } else if (w < 16.000000) {
++            w = sqrt(w) - 3.250000;
++            p = 2.2137376921775787049e-09;
++            p = 9.0756561938885390979e-08 + p * w;
++            p = -2.7517406297064545428e-07 + p * w;
++            p = 1.8239629214389227755e-08 + p * w;
++            p = 1.5027403968909827627e-06 + p * w;
++            p = -4.013867526981545969e-06 + p * w;
++            p = 2.9234449089955446044e-06 + p * w;
++            p = 1.2475304481671778723e-05 + p * w;
++            p = -4.7318229009055733981e-05 + p * w;
++            p = 6.8284851459573175448e-05 + p * w;
++            p = 2.4031110387097893999e-05 + p * w;
++            p = -0.0003550375203628474796 + p * w;
++            p = 0.00095328937973738049703 + p * w;
++            p = -0.0016882755560235047313 + p * w;
++            p = 0.0024914420961078508066 + p * w;
++            p = -0.0037512085075692412107 + p * w;
++            p = 0.005370914553590063617 + p * w;
++            p = 1.0052589676941592334 + p * w;
++            p = 3.0838856104922207635 + p * w;
++        } else {
++            w = sqrt(w) - 5.000000;
++            p = -2.7109920616438573243e-11;
++            p = -2.5556418169965252055e-10 + p * w;
++            p = 1.5076572693500548083e-09 + p * w;
++            p = -3.7894654401267369937e-09 + p * w;
++            p = 7.6157012080783393804e-09 + p * w;
++            p = -1.4960026627149240478e-08 + p * w;
++            p = 2.9147953450901080826e-08 + p * w;
++            p = -6.7711997758452339498e-08 + p * w;
++            p = 2.2900482228026654717e-07 + p * w;
++            p = -9.9298272942317002539e-07 + p * w;
++            p = 4.5260625972231537039e-06 + p * w;
++            p = -1.9681778105531670567e-05 + p * w;
++            p = 7.5995277030017761139e-05 + p * w;
++            p = -0.00021503011930044477347 + p * w;
++            p = -0.00013871931833623122026 + p * w;
++            p = 1.0103004648645343977 + p * w;
++            p = 4.8499064014085844221 + p * w;
++        }
++        return p * x;
++    }
++
++    double standard_deviation(std::vector<double>::iterator first, std::vector<double>::iterator last) {
++        auto m = Catch::Benchmark::Detail::mean(first, last);
++        double variance = std::accumulate(first, last, 0., [m](double a, double b) {
++            double diff = b - m;
++            return a + diff * diff;
++            }) / (last - first);
++            return std::sqrt(variance);
++    }
++
++}
++
++namespace Catch {
++    namespace Benchmark {
++        namespace Detail {
++
++            double weighted_average_quantile(int k, int q, std::vector<double>::iterator first, std::vector<double>::iterator last) {
++                auto count = last - first;
++                double idx = (count - 1) * k / static_cast<double>(q);
++                int j = static_cast<int>(idx);
++                double g = idx - j;
++                std::nth_element(first, first + j, last);
++                auto xj = first[j];
++                if (g == 0) return xj;
++
++                auto xj1 = *std::min_element(first + (j + 1), last);
++                return xj + g * (xj1 - xj);
++            }
++
++            double erfc_inv(double x) {
++                return erf_inv(1.0 - x);
++            }
++
++            double normal_quantile(double p) {
++                static const double ROOT_TWO = std::sqrt(2.0);
++
++                double result = 0.0;
++                assert(p >= 0 && p <= 1);
++                if (p < 0 || p > 1) {
++                    return result;
++                }
++
++                result = -erfc_inv(2.0 * p);
++                // result *= normal distribution standard deviation (1.0) * sqrt(2)
++                result *= /*sd * */ ROOT_TWO;
++                // result += normal disttribution mean (0)
++                return result;
++            }
++
++            double outlier_variance(Estimate<double> mean, Estimate<double> stddev, int n) {
++                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 sg2 = sg * sg;
++                double sb2 = sb * sb;
++
++                auto c_max = [n, mn, sb2, sg2](double x) -> double {
++                    double k = mn - x;
++                    double d = k * k;
++                    double nd = n * d;
++                    double k0 = -n * nd;
++                    double k1 = sb2 - n * sg2 + nd;
++                    double det = k1 * k1 - 4 * sg2 * k0;
++                    return (int)(-2. * k0 / (k1 + std::sqrt(det)));
++                };
++
++                auto var_out = [n, sb2, sg2](double c) {
++                    double nc = n - c;
++                    return (nc / n) * (sb2 - nc * sg2);
++                };
++
++                return (std::min)(var_out(1), var_out((std::min)(c_max(0.), c_max(mg_min)))) / sb2;
++            }
++
++            bootstrap_analysis analyse_samples(double confidence_level, int n_resamples, std::vector<double>::iterator first, std::vector<double>::iterator last) {
++                CATCH_INTERNAL_START_WARNINGS_SUPPRESSION
++                CATCH_INTERNAL_SUPPRESS_GLOBALS_WARNINGS
++                static std::random_device entropy;
++                CATCH_INTERNAL_STOP_WARNINGS_SUPPRESSION
++
++                auto n = static_cast<int>(last - first); // seriously, one can't use integral types without hell in C++
++
++                auto mean = &Detail::mean<std::vector<double>::iterator>;
++                auto stddev = &standard_deviation;
++
++#if defined(CATCH_CONFIG_USE_ASYNC)
++                auto Estimate = [=](double(*f)(std::vector<double>::iterator, std::vector<double>::iterator)) {
++                    auto seed = entropy();
++                    return std::async(std::launch::async, [=] {
++                        std::mt19937 rng(seed);
++                        auto resampled = resample(rng, n_resamples, first, last, f);
++                        return bootstrap(confidence_level, first, last, resampled, f);
++                    });
++                };
++
++                auto mean_future = Estimate(mean);
++                auto stddev_future = Estimate(stddev);
++
++                auto mean_estimate = mean_future.get();
++                auto stddev_estimate = stddev_future.get();
++#else
++                auto Estimate = [=](double(*f)(std::vector<double>::iterator, std::vector<double>::iterator)) {
++                    auto seed = entropy();
++                    std::mt19937 rng(seed);
++                    auto resampled = resample(rng, n_resamples, first, last, f);
++                    return bootstrap(confidence_level, first, last, resampled, f);
++                };
++
++                auto mean_estimate = Estimate(mean);
++                auto stddev_estimate = Estimate(stddev);
++#endif // CATCH_USE_ASYNC
++
++                double outlier_variance = Detail::outlier_variance(mean_estimate, stddev_estimate, n);
++
++                return { mean_estimate, stddev_estimate, outlier_variance };
++            }
++        } // namespace Detail
++    } // namespace Benchmark
++} // namespace Catch
++
++#endif // CATCH_CONFIG_ENABLE_BENCHMARKING
++// end catch_stats.cpp
+ // start catch_approx.cpp
+ 
+ #include <cmath>
+@@ -5365,21 +7901,22 @@
+     bool Approx::equalityComparisonImpl(const double other) const {
+         // First try with fixed margin, then compute margin based on epsilon, scale and Approx's value
+         // Thanks to Richard Harris for his help refining the scaled margin value
+-        return marginComparison(m_value, other, m_margin) || marginComparison(m_value, other, m_epsilon * (m_scale + std::fabs(m_value)));
++        return marginComparison(m_value, other, m_margin)
++            || marginComparison(m_value, other, m_epsilon * (m_scale + std::fabs(std::isinf(m_value)? 0 : m_value)));
+     }
+ 
+-    void Approx::setMargin(double margin) {
+-        CATCH_ENFORCE(margin >= 0,
+-            "Invalid Approx::margin: " << margin << '.'
++    void Approx::setMargin(double newMargin) {
++        CATCH_ENFORCE(newMargin >= 0,
++            "Invalid Approx::margin: " << newMargin << '.'
+             << " Approx::Margin has to be non-negative.");
+-        m_margin = margin;
++        m_margin = newMargin;
+     }
+ 
+-    void Approx::setEpsilon(double epsilon) {
+-        CATCH_ENFORCE(epsilon >= 0 && epsilon <= 1.0,
+-            "Invalid Approx::epsilon: " << epsilon << '.'
++    void Approx::setEpsilon(double newEpsilon) {
++        CATCH_ENFORCE(newEpsilon >= 0 && newEpsilon <= 1.0,
++            "Invalid Approx::epsilon: " << newEpsilon << '.'
+             << " Approx::epsilon has to be in [0, 1]");
+-        m_epsilon = epsilon;
++        m_epsilon = newEpsilon;
+     }
+ 
+ } // end namespace Detail
+@@ -5401,58 +7938,6 @@
+ // end catch_approx.cpp
+ // start catch_assertionhandler.cpp
+ 
+-// start catch_context.h
+-
+-#include <memory>
+-
+-namespace Catch {
+-
+-    struct IResultCapture;
+-    struct IRunner;
+-    struct IConfig;
+-    struct IMutableContext;
+-
+-    using IConfigPtr = std::shared_ptr<IConfig const>;
+-
+-    struct IContext
+-    {
+-        virtual ~IContext();
+-
+-        virtual IResultCapture* getResultCapture() = 0;
+-        virtual IRunner* getRunner() = 0;
+-        virtual IConfigPtr const& getConfig() const = 0;
+-    };
+-
+-    struct IMutableContext : IContext
+-    {
+-        virtual ~IMutableContext();
+-        virtual void setResultCapture( IResultCapture* resultCapture ) = 0;
+-        virtual void setRunner( IRunner* runner ) = 0;
+-        virtual void setConfig( IConfigPtr const& config ) = 0;
+-
+-    private:
+-        static IMutableContext *currentContext;
+-        friend IMutableContext& getCurrentMutableContext();
+-        friend void cleanUpContext();
+-        static void createContext();
+-    };
+-
+-    inline IMutableContext& getCurrentMutableContext()
+-    {
+-        if( !IMutableContext::currentContext )
+-            IMutableContext::createContext();
+-        return *IMutableContext::currentContext;
+-    }
+-
+-    inline IContext& getCurrentContext()
+-    {
+-        return getCurrentMutableContext();
+-    }
+-
+-    void cleanUpContext();
+-}
+-
+-// end catch_context.h
+ // start catch_debugger.h
+ 
+ namespace Catch {
+@@ -5461,7 +7946,24 @@
+ 
+ #ifdef CATCH_PLATFORM_MAC
+ 
+-    #define CATCH_TRAP() __asm__("int $3\n" : : ) /* NOLINT */
++    #if defined(__i386__) || defined(__x86_64__)
++        #define CATCH_TRAP() __asm__("int $3\n" : : ) /* NOLINT */
++    #elif defined(__aarch64__)
++        #define CATCH_TRAP()  __asm__(".inst 0xd4200000")
++    #endif
++
++#elif defined(CATCH_PLATFORM_IPHONE)
++
++    // use inline assembler
++    #if defined(__i386__) || defined(__x86_64__)
++        #define CATCH_TRAP()  __asm__("int $3")
++    #elif defined(__aarch64__)
++        #define CATCH_TRAP()  __asm__(".inst 0xd4200000")
++    #elif defined(__arm__) && !defined(__thumb__)
++        #define CATCH_TRAP()  __asm__(".inst 0xe7f001f0")
++    #elif defined(__arm__) &&  defined(__thumb__)
++        #define CATCH_TRAP()  __asm__(".inst 0xde01")
++    #endif
+ 
+ #elif defined(CATCH_PLATFORM_LINUX)
+     // If we can use inline assembler, do it because this allows us to break
+@@ -5481,13 +7983,12 @@
+     #define CATCH_TRAP() DebugBreak()
+ #endif
+ 
+-#ifdef CATCH_TRAP
+-    #define CATCH_BREAK_INTO_DEBUGGER() if( Catch::isDebuggerActive() ) { CATCH_TRAP(); }
+-#else
+-    namespace Catch {
+-        inline void doNothing() {}
+-    }
+-    #define CATCH_BREAK_INTO_DEBUGGER() Catch::doNothing()
++#ifndef CATCH_BREAK_INTO_DEBUGGER
++    #ifdef CATCH_TRAP
++        #define CATCH_BREAK_INTO_DEBUGGER() []{ if( Catch::isDebuggerActive() ) { CATCH_TRAP(); } }()
++    #else
++        #define CATCH_BREAK_INTO_DEBUGGER() []{}()
++    #endif
+ #endif
+ 
+ // end catch_debugger.h
+@@ -5495,86 +7996,58 @@
+ 
+ // start catch_fatal_condition.h
+ 
+-// start catch_windows_h_proxy.h
+-
+-
+-#if defined(CATCH_PLATFORM_WINDOWS)
+-
+-#if !defined(NOMINMAX) && !defined(CATCH_CONFIG_NO_NOMINMAX)
+-#  define CATCH_DEFINED_NOMINMAX
+-#  define NOMINMAX
+-#endif
+-#if !defined(WIN32_LEAN_AND_MEAN) && !defined(CATCH_CONFIG_NO_WIN32_LEAN_AND_MEAN)
+-#  define CATCH_DEFINED_WIN32_LEAN_AND_MEAN
+-#  define WIN32_LEAN_AND_MEAN
+-#endif
+-
+-#ifdef __AFXDLL
+-#include <AfxWin.h>
+-#else
+-#include <windows.h>
+-#endif
+-
+-#ifdef CATCH_DEFINED_NOMINMAX
+-#  undef NOMINMAX
+-#endif
+-#ifdef CATCH_DEFINED_WIN32_LEAN_AND_MEAN
+-#  undef WIN32_LEAN_AND_MEAN
+-#endif
+-
+-#endif // defined(CATCH_PLATFORM_WINDOWS)
+-
+-// end catch_windows_h_proxy.h
+-#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();
+ 
+-    private:
+-        static bool isSet;
+-        static ULONG guaranteeSize;
+-        static PVOID exceptionHandlerHandle;
+-    };
+-
+-} // 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 );
++        void engage() {
++            assert(!m_started && "Handler cannot be installed twice.");
++            m_started = true;
++            engage_platform();
++        }
+ 
+-        FatalConditionHandler();
+-        ~FatalConditionHandler();
+-        static void reset();
++        void disengage() {
++            assert(m_started && "Handler cannot be uninstalled without being installed first");
++            m_started = false;
++            disengage_platform();
++        }
+     };
+ 
+-} // namespace Catch
+-
+-#else
+-
+-namespace Catch {
+-    struct FatalConditionHandler {
+-        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();
++        }
+     };
+-}
+ 
+-#endif
++} // end namespace Catch
+ 
+ // end catch_fatal_condition.h
+ #include <string>
+@@ -5634,14 +8107,20 @@
+         void sectionEnded( SectionEndInfo const& endInfo ) override;
+         void sectionEndedEarly( SectionEndInfo const& endInfo ) override;
+ 
+-        auto acquireGeneratorTracker( SourceLineInfo const& lineInfo ) -> IGeneratorTracker& override;
++        auto acquireGeneratorTracker( StringRef generatorName, SourceLineInfo const& lineInfo ) -> IGeneratorTracker& override;
+ 
++#if defined(CATCH_CONFIG_ENABLE_BENCHMARKING)
++        void benchmarkPreparing( std::string const& name ) override;
+         void benchmarkStarting( BenchmarkInfo const& info ) override;
+-        void benchmarkEnded( BenchmarkStats const& stats ) override;
++        void benchmarkEnded( BenchmarkStats<> const& stats ) override;
++        void benchmarkFailed( std::string const& error ) override;
++#endif // CATCH_CONFIG_ENABLE_BENCHMARKING
+ 
+         void pushScopedMessage( MessageInfo const& message ) override;
+         void popScopedMessage( MessageInfo const& message ) override;
+ 
++        void emplaceUnscopedMessage( MessageBuilder const& builder ) override;
++
+         std::string getCurrentTestName() const override;
+ 
+         const AssertionResult* getLastResult() const override;
+@@ -5682,22 +8161,26 @@
+         TestRunInfo m_runInfo;
+         IMutableContext& m_context;
+         TestCase const* m_activeTestCase = nullptr;
+-        ITracker* m_testCaseTracker;
++        ITracker* m_testCaseTracker = nullptr;
+         Option<AssertionResult> m_lastResult;
+ 
+         IConfigPtr m_config;
+         Totals m_totals;
+         IStreamingReporterPtr m_reporter;
+         std::vector<MessageInfo> m_messages;
++        std::vector<ScopedMessage> m_messageScopes; /* Keeps owners of so-called unscoped messages. */
+         AssertionInfo m_lastAssertionInfo;
+         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;
+     };
+ 
++    void seedRng(IConfig const& config);
++    unsigned int rngSeed();
+ } // end namespace Catch
+ 
+ // end catch_run_context.h
+@@ -5844,7 +8327,7 @@
+     }
+ 
+     bool AssertionResult::hasExpression() const {
+-        return m_info.capturedExpression[0] != 0;
++        return !m_info.capturedExpression.empty();
+     }
+ 
+     bool AssertionResult::hasMessage() const {
+@@ -5852,16 +8335,22 @@
+     }
+ 
+     std::string AssertionResult::getExpression() const {
+-        if( isFalseTest( m_info.resultDisposition ) )
+-            return "!(" + m_info.capturedExpression + ")";
+-        else
+-            return m_info.capturedExpression;
++        // Possibly overallocating by 3 characters should be basically free
++        std::string expr; expr.reserve(m_info.capturedExpression.size() + 3);
++        if (isFalseTest(m_info.resultDisposition)) {
++            expr += "!(";
++        }
++        expr += m_info.capturedExpression;
++        if (isFalseTest(m_info.resultDisposition)) {
++            expr += ')';
++        }
++        return expr;
+     }
+ 
+     std::string AssertionResult::getExpressionInMacro() const {
+         std::string expr;
+-        if( m_info.macroName[0] == 0 )
+-            expr = m_info.capturedExpression;
++        if( m_info.macroName.empty() )
++            expr = static_cast<std::string>(m_info.capturedExpression);
+         else {
+             expr.reserve( m_info.macroName.size() + m_info.capturedExpression.size() + 4 );
+             expr += m_info.macroName;
+@@ -5896,32 +8385,6 @@
+ 
+ } // end namespace Catch
+ // end catch_assertionresult.cpp
+-// start catch_benchmark.cpp
+-
+-namespace Catch {
+-
+-    auto BenchmarkLooper::getResolution() -> uint64_t {
+-        return getEstimatedClockResolution() * getCurrentContext().getConfig()->benchmarkResolutionMultiple();
+-    }
+-
+-    void BenchmarkLooper::reportStart() {
+-        getResultCapture().benchmarkStarting( { m_name } );
+-    }
+-    auto BenchmarkLooper::needsMoreIterations() -> bool {
+-        auto elapsed = m_timer.getElapsedNanoseconds();
+-
+-        // Exponentially increasing iterations until we're confident in our timer resolution
+-        if( elapsed < m_resolution ) {
+-            m_iterationsToRun *= 10;
+-            return true;
+-        }
+-
+-        getResultCapture().benchmarkEnded( { { m_name }, m_count, elapsed } );
+-        return false;
+-    }
+-
+-} // end namespace Catch
+-// end catch_benchmark.cpp
+ // start catch_capture_matchers.cpp
+ 
+ namespace Catch {
+@@ -6067,6 +8530,9 @@
+ 			m_suffix = false;
+ 			auto width = m_column.m_width - indent();
+ 			m_end = m_pos;
++			if (line()[m_pos] == '\n') {
++				++m_end;
++			}
+ 			while (m_end < line().size() && line()[m_end] != '\n')
+ 				++m_end;
+ 
+@@ -6325,6 +8791,7 @@
+ // ----------- end of #include from clara_textflow.hpp -----------
+ // ........... back in clara.hpp
+ 
++#include <cctype>
+ #include <string>
+ #include <memory>
+ #include <set>
+@@ -6617,7 +9084,7 @@
+     }
+     inline auto convertInto( std::string const &source, bool &target ) -> ParserResult {
+         std::string srcLC = source;
+-        std::transform( srcLC.begin(), srcLC.end(), srcLC.begin(), []( char c ) { return static_cast<char>( ::tolower(c) ); } );
++        std::transform( srcLC.begin(), srcLC.end(), srcLC.begin(), []( unsigned char c ) { return static_cast<char>( std::tolower(c) ); } );
+         if (srcLC == "y" || srcLC == "1" || srcLC == "true" || srcLC == "yes" || srcLC == "on")
+             target = true;
+         else if (srcLC == "n" || srcLC == "0" || srcLC == "false" || srcLC == "no" || srcLC == "off")
+@@ -7265,9 +9732,14 @@
+                     if( !line.empty() && !startsWith( line, '#' ) ) {
+                         if( !startsWith( line, '"' ) )
+                             line = '"' + line + '"';
+-                        config.testsOrTags.push_back( line + ',' );
++                        config.testsOrTags.push_back( line );
++                        config.testsOrTags.emplace_back( "," );
+                     }
+                 }
++                //Remove comma in the end
++                if(!config.testsOrTags.empty())
++                    config.testsOrTags.erase( config.testsOrTags.end()-1 );
++
+                 return ParserResult::ok( ParseResultType::Matched );
+             };
+         auto const setTestOrder = [&]( std::string const& order ) {
+@@ -7302,14 +9774,16 @@
+             };
+         auto const setWaitForKeypress = [&]( std::string const& keypress ) {
+                 auto keypressLc = toLower( keypress );
+-                if( keypressLc == "start" )
++                if (keypressLc == "never")
++                    config.waitForKeypress = WaitForKeypress::Never;
++                else if( keypressLc == "start" )
+                     config.waitForKeypress = WaitForKeypress::BeforeStart;
+                 else if( keypressLc == "exit" )
+                     config.waitForKeypress = WaitForKeypress::BeforeExit;
+                 else if( keypressLc == "both" )
+                     config.waitForKeypress = WaitForKeypress::BeforeStartAndExit;
+                 else
+-                    return ParserResult::runtimeError( "keypress argument must be one of: start, exit or both. '" + keypress + "' not recognised" );
++                    return ParserResult::runtimeError( "keypress argument must be one of: never, start, exit or both. '" + keypress + "' not recognised" );
+             return ParserResult::ok( ParseResultType::Matched );
+             };
+         auto const setVerbosity = [&]( std::string const& verbosity ) {
+@@ -7379,6 +9853,9 @@
+             | Opt( [&]( bool flag ) { config.showDurations = flag ? ShowDurations::Always : ShowDurations::Never; }, "yes|no" )
+                 ["-d"]["--durations"]
+                 ( "show test durations" )
++            | Opt( config.minDuration, "seconds" )
++                ["-D"]["--min-duration"]
++                ( "show test durations for tests taking at least the given number of seconds" )
+             | Opt( loadTestNamesFromFile, "filename" )
+                 ["-f"]["--input-file"]
+                 ( "load test names to run from a file" )
+@@ -7409,13 +9886,24 @@
+             | Opt( config.libIdentify )
+                 ["--libidentify"]
+                 ( "report name and version according to libidentify standard" )
+-            | Opt( setWaitForKeypress, "start|exit|both" )
++            | Opt( setWaitForKeypress, "never|start|exit|both" )
+                 ["--wait-for-keypress"]
+                 ( "waits for a keypress before exiting" )
+-            | Opt( config.benchmarkResolutionMultiple, "multiplier" )
+-                ["--benchmark-resolution-multiple"]
+-                ( "multiple of clock resolution to run benchmarks" )
+-
++            | Opt( config.benchmarkSamples, "samples" )
++                ["--benchmark-samples"]
++                ( "number of samples to collect (default: 100)" )
++            | Opt( config.benchmarkResamples, "resamples" )
++                ["--benchmark-resamples"]
++                ( "number of resamples for the bootstrap (default: 100000)" )
++            | Opt( config.benchmarkConfidenceInterval, "confidence interval" )
++                ["--benchmark-confidence-interval"]
++                ( "confidence interval for the bootstrap (between 0 and 1, default: 0.95)" )
++            | Opt( config.benchmarkNoAnalysis )
++                ["--benchmark-no-analysis"]
++                ( "perform only measurements; do not perform any analysis" )
++            | Opt( config.benchmarkWarmupTime, "benchmarkWarmupTime" )
++                ["--benchmark-warmup-time"]
++                ( "amount of time in milliseconds spent on warming up each test (default: 100)" )
+             | Arg( config.testsOrTags, "test name|pattern|tags" )
+                 ( "which test or tests to use" );
+ 
+@@ -7431,9 +9919,6 @@
+ 
+ namespace Catch {
+ 
+-    bool SourceLineInfo::empty() const noexcept {
+-        return file[0] == '\0';
+-    }
+     bool SourceLineInfo::operator == ( SourceLineInfo const& other ) const noexcept {
+         return line == other.line && (file == other.file || std::strcmp(file, other.file) == 0);
+     }
+@@ -7469,14 +9954,23 @@
+     :   m_data( data ),
+         m_stream( openStream() )
+     {
+-        TestSpecParser parser(ITagAliasRegistry::get());
+-        if (data.testsOrTags.empty()) {
+-            parser.parse("~[.]"); // All not hidden tests
++        // We need to trim filter specs to avoid trouble with superfluous
++        // whitespace (esp. important for bdd macros, as those are manually
++        // aligned with whitespace).
++
++        for (auto& elem : m_data.testsOrTags) {
++            elem = trim(elem);
+         }
+-        else {
++        for (auto& elem : m_data.sectionsToRun) {
++            elem = trim(elem);
++        }
++
++        TestSpecParser parser(ITagAliasRegistry::get());
++        if (!m_data.testsOrTags.empty()) {
+             m_hasTestFilters = true;
+-            for( auto const& testOrTags : data.testsOrTags )
+-                parser.parse( testOrTags );
++            for (auto const& testOrTags : m_data.testsOrTags) {
++                parser.parse(testOrTags);
++            }
+         }
+         m_testSpec = parser.testSpec();
+     }
+@@ -7509,15 +10003,21 @@
+     bool Config::warnAboutMissingAssertions() const    { return !!(m_data.warnings & WarnAbout::NoAssertions); }
+     bool Config::warnAboutNoTests() const              { return !!(m_data.warnings & WarnAbout::NoTests); }
+     ShowDurations::OrNot Config::showDurations() const { return m_data.showDurations; }
++    double Config::minDuration() const                 { return m_data.minDuration; }
+     RunTests::InWhatOrder Config::runOrder() const     { return m_data.runOrder; }
+     unsigned int Config::rngSeed() const               { return m_data.rngSeed; }
+-    int Config::benchmarkResolutionMultiple() const    { return m_data.benchmarkResolutionMultiple; }
+     UseColour::YesOrNo Config::useColour() const       { return m_data.useColour; }
+     bool Config::shouldDebugBreak() const              { return m_data.shouldDebugBreak; }
+     int Config::abortAfter() const                     { return m_data.abortAfter; }
+     bool Config::showInvisibles() const                { return m_data.showInvisibles; }
+     Verbosity Config::verbosity() const                { return m_data.verbosity; }
+ 
++    bool Config::benchmarkNoAnalysis() const                      { return m_data.benchmarkNoAnalysis; }
++    int Config::benchmarkSamples() const                          { return m_data.benchmarkSamples; }
++    double Config::benchmarkConfidenceInterval() const            { return m_data.benchmarkConfidenceInterval; }
++    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);
+     }
+@@ -7546,6 +10046,36 @@
+ }
+ 
+ // end catch_errno_guard.h
++// start catch_windows_h_proxy.h
++
++
++#if defined(CATCH_PLATFORM_WINDOWS)
++
++#if !defined(NOMINMAX) && !defined(CATCH_CONFIG_NO_NOMINMAX)
++#  define CATCH_DEFINED_NOMINMAX
++#  define NOMINMAX
++#endif
++#if !defined(WIN32_LEAN_AND_MEAN) && !defined(CATCH_CONFIG_NO_WIN32_LEAN_AND_MEAN)
++#  define CATCH_DEFINED_WIN32_LEAN_AND_MEAN
++#  define WIN32_LEAN_AND_MEAN
++#endif
++
++#ifdef __AFXDLL
++#include <AfxWin.h>
++#else
++#include <windows.h>
++#endif
++
++#ifdef CATCH_DEFINED_NOMINMAX
++#  undef NOMINMAX
++#endif
++#ifdef CATCH_DEFINED_WIN32_LEAN_AND_MEAN
++#  undef WIN32_LEAN_AND_MEAN
++#endif
++
++#endif // defined(CATCH_PLATFORM_WINDOWS)
++
++// end catch_windows_h_proxy.h
+ #include <sstream>
+ 
+ namespace Catch {
+@@ -7557,7 +10087,7 @@
+         };
+ 
+         struct NoColourImpl : IColourImpl {
+-            void use( Colour::Code ) {}
++            void use( Colour::Code ) override {}
+ 
+             static IColourImpl* instance() {
+                 static NoColourImpl s_instance;
+@@ -7591,7 +10121,7 @@
+             originalBackgroundAttributes = csbiInfo.wAttributes & ~( FOREGROUND_GREEN | FOREGROUND_RED | FOREGROUND_BLUE | FOREGROUND_INTENSITY );
+         }
+ 
+-        virtual void use( Colour::Code _colourCode ) override {
++        void use( Colour::Code _colourCode ) override {
+             switch( _colourCode ) {
+                 case Colour::None:      return setTextAttribute( originalForegroundAttributes );
+                 case Colour::White:     return setTextAttribute( FOREGROUND_GREEN | FOREGROUND_RED | FOREGROUND_BLUE );
+@@ -7654,7 +10184,7 @@
+     // https://github.com/philsquared/Catch/pull/131
+     class PosixColourImpl : public IColourImpl {
+     public:
+-        virtual void use( Colour::Code _colourCode ) override {
++        void use( Colour::Code _colourCode ) override {
+             switch( _colourCode ) {
+                 case Colour::None:
+                 case Colour::White:     return setColour( "[0m" );
+@@ -7682,13 +10212,14 @@
+ 
+     private:
+         void setColour( const char* _escapeCode ) {
+-            Catch::cout() << '\033' << _escapeCode;
++            getCurrentContext().getConfig()->stream()
++                << '\033' << _escapeCode;
+         }
+     };
+ 
+     bool useColourOnPlatform() {
+         return
+-#ifdef CATCH_PLATFORM_MAC
++#if defined(CATCH_PLATFORM_MAC) || defined(CATCH_PLATFORM_IPHONE)
+             !isDebuggerActive() &&
+ #endif
+ #if !(defined(__DJGPP__) && defined(__STRICT_ANSI__))
+@@ -7729,13 +10260,13 @@
+ namespace Catch {
+ 
+     Colour::Colour( Code _colourCode ) { use( _colourCode ); }
+-    Colour::Colour( Colour&& rhs ) noexcept {
+-        m_moved = rhs.m_moved;
+-        rhs.m_moved = true;
+-    }
+-    Colour& Colour::operator=( Colour&& rhs ) noexcept {
+-        m_moved = rhs.m_moved;
+-        rhs.m_moved  = true;
++    Colour::Colour( Colour&& other ) noexcept {
++        m_moved = other.m_moved;
++        other.m_moved = true;
++    }
++    Colour& Colour::operator=( Colour&& other ) noexcept {
++        m_moved = other.m_moved;
++        other.m_moved  = true;
+         return *this;
+     }
+ 
+@@ -7743,7 +10274,13 @@
+ 
+     void Colour::use( Code _colourCode ) {
+         static IColourImpl* impl = platformColourInstance();
+-        impl->use( _colourCode );
++        // Strictly speaking, this cannot possibly happen.
++        // However, under some conditions it does happen (see #1626),
++        // and this change is small enough that we can let practicality
++        // triumph over purity in this case.
++        if (impl != nullptr) {
++            impl->use( _colourCode );
++        }
+     }
+ 
+     std::ostream& operator << ( std::ostream& os, Colour const& ) {
+@@ -7764,27 +10301,27 @@
+     class Context : public IMutableContext, NonCopyable {
+ 
+     public: // IContext
+-        virtual IResultCapture* getResultCapture() override {
++        IResultCapture* getResultCapture() override {
+             return m_resultCapture;
+         }
+-        virtual IRunner* getRunner() override {
++        IRunner* getRunner() override {
+             return m_runner;
+         }
+ 
+-        virtual IConfigPtr const& getConfig() const override {
++        IConfigPtr const& getConfig() const override {
+             return m_config;
+         }
+ 
+-        virtual ~Context() override;
++        ~Context() override;
+ 
+     public: // IMutableContext
+-        virtual void setResultCapture( IResultCapture* resultCapture ) override {
++        void setResultCapture( IResultCapture* resultCapture ) override {
+             m_resultCapture = resultCapture;
+         }
+-        virtual void setRunner( IRunner* runner ) override {
++        void setRunner( IRunner* runner ) override {
+             m_runner = runner;
+         }
+-        virtual void setConfig( IConfigPtr const& config ) override {
++        void setConfig( IConfigPtr const& config ) override {
+             m_config = config;
+         }
+ 
+@@ -7810,6 +10347,12 @@
+     IContext::~IContext() = default;
+     IMutableContext::~IMutableContext() = default;
+     Context::~Context() = default;
++
++    SimplePcg32& rng() {
++        static SimplePcg32 s_rng;
++        return s_rng;
++    }
++
+ }
+ // end catch_context.cpp
+ // start catch_debug_console.cpp
+@@ -7823,7 +10366,16 @@
+ }
+ 
+ // end catch_debug_console.h
+-#ifdef CATCH_PLATFORM_WINDOWS
++#if defined(CATCH_CONFIG_ANDROID_LOGWRITE)
++#include <android/log.h>
++
++    namespace Catch {
++        void writeToDebugConsole( std::string const& text ) {
++            __android_log_write( ANDROID_LOG_DEBUG, "Catch", text.c_str() );
++        }
++    }
++
++#elif defined(CATCH_PLATFORM_WINDOWS)
+ 
+     namespace Catch {
+         void writeToDebugConsole( std::string const& text ) {
+@@ -7844,25 +10396,28 @@
+ // end catch_debug_console.cpp
+ // start catch_debugger.cpp
+ 
+-#ifdef CATCH_PLATFORM_MAC
++#if defined(CATCH_PLATFORM_MAC) || defined(CATCH_PLATFORM_IPHONE)
+ 
+-#  include <assert.h>
+-#  include <stdbool.h>
++#  include <cassert>
+ #  include <sys/types.h>
+ #  include <unistd.h>
+-#  include <sys/sysctl.h>
+ #  include <cstddef>
+ #  include <ostream>
+ 
+-namespace Catch {
++#ifdef __apple_build_version__
++    // These headers will only compile with AppleClang (XCode)
++    // For other compilers (Clang, GCC, ... ) we need to exclude them
++#  include <sys/sysctl.h>
++#endif
+ 
++    namespace Catch {
++        #ifdef __apple_build_version__
+         // The following function is taken directly from the following technical note:
+-        // http://developer.apple.com/library/mac/#qa/qa2004/qa1361.html
++        // https://developer.apple.com/library/archive/qa/qa1361/_index.html
+ 
+         // Returns true if the current process is being debugged (either
+         // running under the debugger or has a debugger attached post facto).
+         bool isDebuggerActive(){
+-
+             int                 mib[4];
+             struct kinfo_proc   info;
+             std::size_t         size;
+@@ -7892,6 +10447,12 @@
+ 
+             return ( (info.kp_proc.p_flag & P_TRACED) != 0 );
+         }
++        #else
++        bool isDebuggerActive() {
++            // We need to find another way to determine this for non-appleclang compilers on macOS
++            return false;
++        }
++        #endif
+     } // namespace Catch
+ 
+ #elif defined(CATCH_PLATFORM_LINUX)
+@@ -7962,6 +10523,8 @@
+ // end catch_decomposer.cpp
+ // start catch_enforce.cpp
+ 
++#include <stdexcept>
++
+ namespace Catch {
+ #if defined(CATCH_CONFIG_DISABLE_EXCEPTIONS) && !defined(CATCH_CONFIG_DISABLE_EXCEPTIONS_CUSTOM_HANDLER)
+     [[noreturn]]
+@@ -7971,8 +10534,116 @@
+         std::terminate();
+     }
+ #endif
++
++    [[noreturn]]
++    void throw_logic_error(std::string const& msg) {
++        throw_exception(std::logic_error(msg));
++    }
++
++    [[noreturn]]
++    void throw_domain_error(std::string const& msg) {
++        throw_exception(std::domain_error(msg));
++    }
++
++    [[noreturn]]
++    void throw_runtime_error(std::string const& msg) {
++        throw_exception(std::runtime_error(msg));
++    }
++
+ } // namespace Catch;
+ // end catch_enforce.cpp
++// start catch_enum_values_registry.cpp
++// start catch_enum_values_registry.h
++
++#include <vector>
++#include <memory>
++
++namespace Catch {
++
++    namespace Detail {
++
++        std::unique_ptr<EnumInfo> makeEnumInfo( StringRef enumName, StringRef allValueNames, std::vector<int> const& values );
++
++        class EnumValuesRegistry : public IMutableEnumValuesRegistry {
++
++            std::vector<std::unique_ptr<EnumInfo>> m_enumInfos;
++
++            EnumInfo const& registerEnum( StringRef enumName, StringRef allEnums, std::vector<int> const& values) override;
++        };
++
++        std::vector<StringRef> parseEnums( StringRef enums );
++
++    } // Detail
++
++} // Catch
++
++// end catch_enum_values_registry.h
++
++#include <map>
++#include <cassert>
++
++namespace Catch {
++
++    IMutableEnumValuesRegistry::~IMutableEnumValuesRegistry() {}
++
++    namespace Detail {
++
++        namespace {
++            // 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 occurrence of ":"
++                size_t name_start = enumInstance.size();
++                while (name_start > 0 && enumInstance[name_start - 1] != ':') {
++                    --name_start;
++                }
++                return enumInstance.substr(name_start, enumInstance.size() - name_start);
++            }
++        }
++
++        std::vector<StringRef> parseEnums( StringRef enums ) {
++            auto enumValues = splitStringRef( enums, ',' );
++            std::vector<StringRef> parsed;
++            parsed.reserve( enumValues.size() );
++            for( auto const& enumValue : enumValues ) {
++                parsed.push_back(trim(extractInstanceName(enumValue)));
++            }
++            return parsed;
++        }
++
++        EnumInfo::~EnumInfo() {}
++
++        StringRef EnumInfo::lookup( int value ) const {
++            for( auto const& valueToName : m_values ) {
++                if( valueToName.first == value )
++                    return valueToName.second;
++            }
++            return "{** unexpected enum value **}"_sr;
++        }
++
++        std::unique_ptr<EnumInfo> makeEnumInfo( StringRef enumName, StringRef allValueNames, std::vector<int> const& values ) {
++            std::unique_ptr<EnumInfo> enumInfo( new EnumInfo );
++            enumInfo->m_name = enumName;
++            enumInfo->m_values.reserve( values.size() );
++
++            const auto valueNames = Catch::Detail::parseEnums( allValueNames );
++            assert( valueNames.size() == values.size() );
++            std::size_t i = 0;
++            for( auto value : values )
++                enumInfo->m_values.emplace_back(value, valueNames[i++]);
++
++            return enumInfo;
++        }
++
++        EnumInfo const& EnumValuesRegistry::registerEnum( StringRef enumName, StringRef allValueNames, std::vector<int> const& values ) {
++            m_enumInfos.push_back(makeEnumInfo(enumName, allValueNames, values));
++            return *m_enumInfos.back();
++        }
++
++    } // Detail
++} // Catch
++
++// end catch_enum_values_registry.cpp
+ // start catch_errno_guard.cpp
+ 
+ #include <cerrno>
+@@ -7996,7 +10667,7 @@
+     public:
+         ~ExceptionTranslatorRegistry();
+         virtual void registerTranslator( const IExceptionTranslator* translator );
+-        virtual std::string translateActiveException() const override;
++        std::string translateActiveException() const override;
+         std::string tryTranslators() const;
+ 
+     private:
+@@ -8061,54 +10732,82 @@
+         }
+     }
+ 
++    std::string ExceptionTranslatorRegistry::tryTranslators() const {
++        if (m_translators.empty()) {
++            std::rethrow_exception(std::current_exception());
++        } else {
++            return m_translators[0]->translate(m_translators.begin() + 1, m_translators.end());
++        }
++    }
++
+ #else // ^^ Exceptions are enabled // Exceptions are disabled vv
+     std::string ExceptionTranslatorRegistry::translateActiveException() const {
+         CATCH_INTERNAL_ERROR("Attempted to translate active exception under CATCH_CONFIG_DISABLE_EXCEPTIONS!");
+     }
+-#endif
+ 
+     std::string ExceptionTranslatorRegistry::tryTranslators() const {
+-        if( m_translators.empty() )
+-            std::rethrow_exception(std::current_exception());
+-        else
+-            return m_translators[0]->translate( m_translators.begin()+1, m_translators.end() );
++        CATCH_INTERNAL_ERROR("Attempted to use exception translators under CATCH_CONFIG_DISABLE_EXCEPTIONS!");
+     }
++#endif
++
+ }
+ // end catch_exception_translator_registry.cpp
+ // start catch_fatal_condition.cpp
+ 
+-#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 anecdotally, 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.
+     // Windows can easily distinguish between SO and SigSegV,
+     // but SigInt, SigTerm, etc are handled differently.
+     static SignalDefs signalDefs[] = {
+-        { EXCEPTION_ILLEGAL_INSTRUCTION,  "SIGILL - Illegal instruction signal" },
+-        { EXCEPTION_STACK_OVERFLOW, "SIGSEGV - Stack overflow" },
+-        { EXCEPTION_ACCESS_VIOLATION, "SIGSEGV - Segmentation violation signal" },
+-        { EXCEPTION_INT_DIVIDE_BY_ZERO, "Divide by zero error" },
++        { static_cast<DWORD>(EXCEPTION_ILLEGAL_INSTRUCTION),  "SIGILL - Illegal instruction signal" },
++        { static_cast<DWORD>(EXCEPTION_STACK_OVERFLOW), "SIGSEGV - Stack overflow" },
++        { static_cast<DWORD>(EXCEPTION_ACCESS_VIOLATION), "SIGSEGV - Segmentation violation signal" },
++        { 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);
+@@ -8119,38 +10818,50 @@
+         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;
+     }
+ 
+-    FatalConditionHandler::~FatalConditionHandler() {
+-        reset();
+-    }
++} // end namespace Catch
+ 
+-bool FatalConditionHandler::isSet = false;
+-ULONG FatalConditionHandler::guaranteeSize = 0;
+-PVOID FatalConditionHandler::exceptionHandlerHandle = nullptr;
++#endif // CATCH_CONFIG_WINDOWS_SEH
+ 
+-} // namespace Catch
++#if defined( CATCH_CONFIG_POSIX_SIGNALS )
+ 
+-#elif defined( CATCH_CONFIG_POSIX_SIGNALS )
++#include <signal.h>
+ 
+ namespace Catch {
+ 
+@@ -8159,10 +10870,6 @@
+         const char* name;
+     };
+ 
+-    // 32kb for the alternate stack seems to be sufficient. However, this value
+-    // is experimentally determined, so that's not guaranteed.
+-    constexpr static std::size_t sigStackSize = 32768 >= MINSIGSTKSZ ? 32768 : MINSIGSTKSZ;
+-
+     static SignalDefs signalDefs[] = {
+         { SIGINT,  "SIGINT - Terminal interrupt signal" },
+         { SIGILL,  "SIGILL - Illegal instruction signal" },
+@@ -8172,7 +10879,32 @@
+         { SIGABRT, "SIGABRT - Abort (abnormal termination) signal" }
+     };
+ 
+-    void FatalConditionHandler::handleSignal( int sig ) {
++// 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);
++    }
++
++    static void handleSignal( int sig ) {
+         char const * name = "<unknown signal>";
+         for (auto const& def : signalDefs) {
+             if (sig == def.id) {
+@@ -8180,16 +10912,33 @@
+                 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 = { };
+@@ -8201,59 +10950,20 @@
+         }
+     }
+ 
+-    FatalConditionHandler::~FatalConditionHandler() {
+-        reset();
+-    }
+-
+-    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] = {};
+-
+-} // namespace Catch
+-
+-#else
+-
+-namespace Catch {
+-    void FatalConditionHandler::reset() {}
+-}
+-
+-#endif // signals/SEH handling
+-
+ #if defined(__GNUC__)
+ #    pragma GCC diagnostic pop
+ #endif
+-// end catch_fatal_condition.cpp
+-// start catch_generators.cpp
+-
+-// start catch_random_number_generator.h
+-
+-#include <algorithm>
+-#include <random>
+-
+-namespace Catch {
+ 
+-    struct IConfig;
++    void FatalConditionHandler::disengage_platform() {
++        restorePreviousSignalHandlers();
++    }
+ 
+-    std::mt19937& rng();
+-    void seedRng( IConfig const& config );
+-    unsigned int rngSeed();
++} // end namespace Catch
+ 
+-}
++#endif // CATCH_CONFIG_POSIX_SIGNALS
++// end catch_fatal_condition.cpp
++// start catch_generators.cpp
+ 
+-// end catch_random_number_generator.h
+ #include <limits>
+ #include <set>
+ 
+@@ -8261,34 +10971,16 @@
+ 
+ IGeneratorTracker::~IGeneratorTracker() {}
+ 
+-namespace Generators {
+-
+-    GeneratorBase::~GeneratorBase() {}
+-
+-    std::vector<size_t> randomiseIndices( size_t selectionSize, size_t sourceSize ) {
+-
+-        assert( selectionSize <= sourceSize );
+-        std::vector<size_t> indices;
+-        indices.reserve( selectionSize );
+-        std::uniform_int_distribution<size_t> uid( 0, sourceSize-1 );
++const char* GeneratorException::what() const noexcept {
++    return m_msg;
++}
+ 
+-        std::set<size_t> seen;
+-        // !TBD: improve this algorithm
+-        while( indices.size() < selectionSize ) {
+-            auto index = uid( rng() );
+-            if( seen.insert( index ).second )
+-                indices.push_back( index );
+-        }
+-        return indices;
+-    }
++namespace Generators {
+ 
+-    auto acquireGeneratorTracker( SourceLineInfo const& lineInfo ) -> IGeneratorTracker& {
+-        return getResultCapture().acquireGeneratorTracker( lineInfo );
+-    }
++    GeneratorUntypedBase::~GeneratorUntypedBase() {}
+ 
+-    template<>
+-    auto all<int>() -> Generator<int> {
+-        return range( std::numeric_limits<int>::min(), std::numeric_limits<int>::max() );
++    auto acquireGeneratorTracker( StringRef generatorName, SourceLineInfo const& lineInfo ) -> IGeneratorTracker& {
++        return getResultCapture().acquireGeneratorTracker( generatorName, lineInfo );
+     }
+ 
+ } // namespace Generators
+@@ -8344,10 +11036,16 @@
+ 
+         void noMatchingTestCases( std::string const& spec ) override;
+ 
++        void reportInvalidArguments(std::string const&arg) override;
++
+         static std::set<Verbosity> getSupportedVerbosities();
+ 
++#if defined(CATCH_CONFIG_ENABLE_BENCHMARKING)
++        void benchmarkPreparing(std::string const& name) override;
+         void benchmarkStarting( BenchmarkInfo const& benchmarkInfo ) override;
+-        void benchmarkEnded( BenchmarkStats const& benchmarkStats ) override;
++        void benchmarkEnded( BenchmarkStats<> const& benchmarkStats ) override;
++        void benchmarkFailed(std::string const&) override;
++#endif // CATCH_CONFIG_ENABLE_BENCHMARKING
+ 
+         void testRunStarting( TestRunInfo const& testRunInfo ) override;
+         void testGroupStarting( GroupInfo const& groupInfo ) override;
+@@ -8538,7 +11236,7 @@
+ 
+     std::size_t listReporters();
+ 
+-    Option<std::size_t> list( Config const& config );
++    Option<std::size_t> list( std::shared_ptr<Config> const& config );
+ 
+ } // end namespace Catch
+ 
+@@ -8557,7 +11255,7 @@
+ namespace Catch {
+ 
+     std::size_t listTests( Config const& config ) {
+-        TestSpec testSpec = config.testSpec();
++        TestSpec const& testSpec = config.testSpec();
+         if( config.hasTestFilters() )
+             Catch::cout() << "Matching test cases:\n";
+         else {
+@@ -8591,7 +11289,7 @@
+     }
+ 
+     std::size_t listTestsNamesOnly( Config const& config ) {
+-        TestSpec testSpec = config.testSpec();
++        TestSpec const& testSpec = config.testSpec();
+         std::size_t matchedTests = 0;
+         std::vector<TestCase> matchedTestCases = filterTests( getAllTestCasesSorted( config ), testSpec, config );
+         for( auto const& testCaseInfo : matchedTestCases ) {
+@@ -8613,14 +11311,23 @@
+     }
+ 
+     std::string TagInfo::all() const {
+-        std::string out;
+-        for( auto const& spelling : spellings )
+-            out += "[" + spelling + "]";
++        size_t size = 0;
++        for (auto const& spelling : spellings) {
++            // Add 2 for the brackes
++            size += spelling.size() + 2;
++        }
++
++        std::string out; out.reserve(size);
++        for (auto const& spelling : spellings) {
++            out += '[';
++            out += spelling;
++            out += ']';
++        }
+         return out;
+     }
+ 
+     std::size_t listTags( Config const& config ) {
+-        TestSpec testSpec = config.testSpec();
++        TestSpec const& testSpec = config.testSpec();
+         if( config.hasTestFilters() )
+             Catch::cout() << "Tags for matching test cases:\n";
+         else {
+@@ -8676,15 +11383,16 @@
+         return factories.size();
+     }
+ 
+-    Option<std::size_t> list( Config const& config ) {
++    Option<std::size_t> list( std::shared_ptr<Config> const& config ) {
+         Option<std::size_t> listedCount;
+-        if( config.listTests() )
+-            listedCount = listedCount.valueOr(0) + listTests( config );
+-        if( config.listTestNamesOnly() )
+-            listedCount = listedCount.valueOr(0) + listTestsNamesOnly( config );
+-        if( config.listTags() )
+-            listedCount = listedCount.valueOr(0) + listTags( config );
+-        if( config.listReporters() )
++        getCurrentMutableContext().setConfig( config );
++        if( config->listTests() )
++            listedCount = listedCount.valueOr(0) + listTests( *config );
++        if( config->listTestNamesOnly() )
++            listedCount = listedCount.valueOr(0) + listTestsNamesOnly( *config );
++        if( config->listTags() )
++            listedCount = listedCount.valueOr(0) + listTags( *config );
++        if( config->listReporters() )
+             listedCount = listedCount.valueOr(0) + listReporters();
+         return listedCount;
+     }
+@@ -8713,6 +11421,29 @@
+ 
+ } // namespace Catch
+ // end catch_matchers.cpp
++// start catch_matchers_exception.cpp
++
++namespace Catch {
++namespace Matchers {
++namespace Exception {
++
++bool ExceptionMessageMatcher::match(std::exception const& ex) const {
++    return ex.what() == m_message;
++}
++
++std::string ExceptionMessageMatcher::describe() const {
++    return "exception message matches \"" + m_message + "\"";
++}
++
++}
++Exception::ExceptionMessageMatcher Message(std::string const& message) {
++    return Exception::ExceptionMessageMatcher(message);
++}
++
++// namespace Exception
++} // namespace Matchers
++} // namespace Catch
++// end catch_matchers_exception.cpp
+ // start catch_matchers_floating.cpp
+ 
+ // start catch_polyfills.hpp
+@@ -8741,74 +11472,101 @@
+ } // end namespace Catch
+ 
+ // end catch_to_string.hpp
++#include <algorithm>
++#include <cmath>
+ #include <cstdlib>
+ #include <cstdint>
+ #include <cstring>
++#include <sstream>
++#include <type_traits>
++#include <iomanip>
++#include <limits>
+ 
+ namespace Catch {
+-namespace Matchers {
+-namespace Floating {
+-enum class FloatingPointKind : uint8_t {
+-    Float,
+-    Double
+-};
+-}
+-}
+-}
+-
+ namespace {
+ 
+-template <typename T>
+-struct Converter;
+-
+-template <>
+-struct Converter<float> {
+-    static_assert(sizeof(float) == sizeof(int32_t), "Important ULP matcher assumption violated");
+-    Converter(float f) {
++    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;
+     }
+-    int32_t i;
+-};
+ 
+-template <>
+-struct Converter<double> {
+-    static_assert(sizeof(double) == sizeof(int64_t), "Important ULP matcher assumption violated");
+-    Converter(double d) {
++    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;
+     }
+-    int64_t i;
+-};
+ 
+-template <typename T>
+-auto convert(T t) -> Converter<T> {
+-    return Converter<T>(t);
+-}
++    template <typename FP>
++    bool almostEqualUlps(FP lhs, FP rhs, uint64_t maxUlpDiff) {
++        // Comparison with NaN should always be false.
++        // This way we can rule it out before getting into the ugly details
++        if (Catch::isnan(lhs) || Catch::isnan(rhs)) {
++            return false;
++        }
+ 
+-template <typename FP>
+-bool almostEqualUlps(FP lhs, FP rhs, int maxUlpDiff) {
+-    // Comparison with NaN should always be false.
+-    // This way we can rule it out before getting into the ugly details
+-    if (Catch::isnan(lhs) || Catch::isnan(rhs)) {
+-        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;
++        }
++
++        // static cast as a workaround for IBM XLC
++        auto ulpDiff = std::abs(static_cast<FP>(lc - rc));
++        return static_cast<uint64_t>(ulpDiff) <= maxUlpDiff;
+     }
+ 
+-    auto lc = convert(lhs);
+-    auto rc = convert(rhs);
++#if defined(CATCH_CONFIG_GLOBAL_NEXTAFTER)
++
++    float nextafter(float x, float y) {
++        return ::nextafterf(x, y);
++    }
+ 
+-    if ((lc.i < 0) != (rc.i < 0)) {
+-        // Potentially we can have +0 and -0
+-        return lhs == rhs;
++    double nextafter(double x, double y) {
++        return ::nextafter(x, y);
+     }
+ 
+-    auto ulpDiff = std::abs(lc.i - rc.i);
+-    return ulpDiff <= maxUlpDiff;
++#endif // ^^^ CATCH_CONFIG_GLOBAL_NEXTAFTER ^^^
++
++template <typename FP>
++FP step(FP start, FP direction, uint64_t steps) {
++    for (uint64_t i = 0; i < steps; ++i) {
++#if defined(CATCH_CONFIG_GLOBAL_NEXTAFTER)
++        start = Catch::nextafter(start, direction);
++#else
++        start = std::nextafter(start, direction);
++#endif
++    }
++    return start;
++}
++
++// Performs equivalent check of std::fabs(lhs - rhs) <= margin
++// But without the subtraction to allow for INFINITY in comparison
++bool marginComparison(double lhs, double rhs, double margin) {
++    return (lhs + margin >= rhs) && (rhs + margin >= lhs);
+ }
+ 
++template <typename FloatingPoint>
++void write(std::ostream& out, FloatingPoint num) {
++    out << std::scientific
++        << std::setprecision(std::numeric_limits<FloatingPoint>::max_digits10 - 1)
++        << num;
+ }
+ 
+-namespace Catch {
++} // end anonymous namespace
++
+ namespace Matchers {
+ namespace Floating {
++
++    enum class FloatingPointKind : uint8_t {
++        Float,
++        Double
++    };
++
+     WithinAbsMatcher::WithinAbsMatcher(double target, double margin)
+         :m_target{ target }, m_margin{ margin } {
+         CATCH_ENFORCE(margin >= 0, "Invalid margin: " << margin << '.'
+@@ -8825,10 +11583,11 @@
+         return "is within " + ::Catch::Detail::stringify(m_margin) + " of " + ::Catch::Detail::stringify(m_target);
+     }
+ 
+-    WithinUlpsMatcher::WithinUlpsMatcher(double target, int ulps, FloatingPointKind baseType)
++    WithinUlpsMatcher::WithinUlpsMatcher(double target, uint64_t ulps, FloatingPointKind baseType)
+         :m_target{ target }, m_ulps{ ulps }, m_type{ baseType } {
+-        CATCH_ENFORCE(ulps >= 0, "Invalid ULP setting: " << ulps << '.'
+-            << " ULPs have to be non-negative.");
++        CATCH_ENFORCE(m_type == FloatingPointKind::Double
++                   || m_ulps < (std::numeric_limits<uint32_t>::max)(),
++            "Provided ULP is impossibly large for a float comparison.");
+     }
+ 
+ #if defined(__clang__)
+@@ -8853,16 +11612,59 @@
+ #endif
+ 
+     std::string WithinUlpsMatcher::describe() const {
+-        return "is within " + Catch::to_string(m_ulps) + " ULPs of " + ::Catch::Detail::stringify(m_target) + ((m_type == FloatingPointKind::Float)? "f" : "");
++        std::stringstream ret;
++
++        ret << "is within " << m_ulps << " ULPs of ";
++
++        if (m_type == FloatingPointKind::Float) {
++            write(ret, static_cast<float>(m_target));
++            ret << 'f';
++        } else {
++            write(ret, m_target);
++        }
++
++        ret << " ([";
++        if (m_type == FloatingPointKind::Double) {
++            write(ret, step(m_target, static_cast<double>(-INFINITY), m_ulps));
++            ret << ", ";
++            write(ret, step(m_target, static_cast<double>( INFINITY), m_ulps));
++        } else {
++            // We have to cast INFINITY to float because of MinGW, see #1782
++            write(ret, step(static_cast<float>(m_target), static_cast<float>(-INFINITY), m_ulps));
++            ret << ", ";
++            write(ret, step(static_cast<float>(m_target), static_cast<float>( INFINITY), m_ulps));
++        }
++        ret << "])";
++
++        return ret.str();
++    }
++
++    WithinRelMatcher::WithinRelMatcher(double target, double epsilon):
++        m_target(target),
++        m_epsilon(epsilon){
++        CATCH_ENFORCE(m_epsilon >= 0., "Relative comparison with epsilon <  0 does not make sense.");
++        CATCH_ENFORCE(m_epsilon  < 1., "Relative comparison with epsilon >= 1 does not make sense.");
++    }
++
++    bool WithinRelMatcher::match(double const& matchee) const {
++        const auto relMargin = m_epsilon * (std::max)(std::fabs(matchee), std::fabs(m_target));
++        return marginComparison(matchee, m_target,
++                                std::isinf(relMargin)? 0 : relMargin);
++    }
++
++    std::string WithinRelMatcher::describe() const {
++        Catch::ReusableStringStream sstr;
++        sstr << "and " << m_target << " are within " << m_epsilon * 100. << "% of each other";
++        return sstr.str();
+     }
+ 
+ }// namespace Floating
+ 
+-Floating::WithinUlpsMatcher WithinULP(double target, int maxUlpDiff) {
++Floating::WithinUlpsMatcher WithinULP(double target, uint64_t maxUlpDiff) {
+     return Floating::WithinUlpsMatcher(target, maxUlpDiff, Floating::FloatingPointKind::Double);
+ }
+ 
+-Floating::WithinUlpsMatcher WithinULP(float target, int maxUlpDiff) {
++Floating::WithinUlpsMatcher WithinULP(float target, uint64_t maxUlpDiff) {
+     return Floating::WithinUlpsMatcher(target, maxUlpDiff, Floating::FloatingPointKind::Float);
+ }
+ 
+@@ -8870,9 +11672,24 @@
+     return Floating::WithinAbsMatcher(target, margin);
+ }
+ 
++Floating::WithinRelMatcher WithinRel(double target, double eps) {
++    return Floating::WithinRelMatcher(target, eps);
++}
++
++Floating::WithinRelMatcher WithinRel(double target) {
++    return Floating::WithinRelMatcher(target, std::numeric_limits<double>::epsilon() * 100);
++}
++
++Floating::WithinRelMatcher WithinRel(float target, float eps) {
++    return Floating::WithinRelMatcher(target, eps);
++}
++
++Floating::WithinRelMatcher WithinRel(float target) {
++    return Floating::WithinRelMatcher(target, std::numeric_limits<float>::epsilon() * 100);
++}
++
+ } // namespace Matchers
+ } // namespace Catch
+-
+ // end catch_matchers_floating.cpp
+ // start catch_matchers_generic.cpp
+ 
+@@ -9030,28 +11847,43 @@
+     ////////////////////////////////////////////////////////////////////////////
+ 
+     ScopedMessage::ScopedMessage( MessageBuilder const& builder )
+-    : m_info( builder.m_info )
++    : m_info( builder.m_info ), m_moved()
+     {
+         m_info.message = builder.m_stream.str();
+         getResultCapture().pushScopedMessage( m_info );
+     }
+ 
++    ScopedMessage::ScopedMessage( ScopedMessage&& old )
++    : m_info( old.m_info ), m_moved()
++    {
++        old.m_moved = true;
++    }
++
+     ScopedMessage::~ScopedMessage() {
+-        if ( !uncaught_exceptions() ){
++        if ( !uncaught_exceptions() && !m_moved ){
+             getResultCapture().popScopedMessage(m_info);
+         }
+     }
+ 
+     Capturer::Capturer( StringRef macroName, SourceLineInfo const& lineInfo, ResultWas::OfType resultType, StringRef names ) {
+         auto trimmed = [&] (size_t start, size_t end) {
+-            while (names[start] == ',' || isspace(names[start])) {
++            while (names[start] == ',' || isspace(static_cast<unsigned char>(names[start]))) {
+                 ++start;
+             }
+-            while (names[end] == ',' || isspace(names[end])) {
++            while (names[end] == ',' || isspace(static_cast<unsigned char>(names[end]))) {
+                 --end;
+             }
+             return names.substr(start, end - start + 1);
+         };
++        auto skipq = [&] (size_t start, char quote) {
++            for (auto i = start + 1; i < names.size() ; ++i) {
++                if (names[i] == quote)
++                    return i;
++                if (names[i] == '\\')
++                    ++i;
++            }
++            CATCH_INTERNAL_ERROR("CAPTURE parsing encountered unmatched quote");
++        };
+ 
+         size_t start = 0;
+         std::stack<char> openings;
+@@ -9072,18 +11904,22 @@
+ //           case '>':
+                 openings.pop();
+                 break;
++            case '"':
++            case '\'':
++                pos = skipq(pos, c);
++                break;
+             case ',':
+-                if (start != pos && openings.size() == 0) {
++                if (start != pos && openings.empty()) {
+                     m_messages.emplace_back(macroName, lineInfo, resultType);
+-                    m_messages.back().message = trimmed(start, pos);
++                    m_messages.back().message = static_cast<std::string>(trimmed(start, pos));
+                     m_messages.back().message += " := ";
+                     start = pos;
+                 }
+             }
+         }
+-        assert(openings.size() == 0 && "Mismatched openings");
++        assert(openings.empty() && "Mismatched openings");
+         m_messages.emplace_back(macroName, lineInfo, resultType);
+-        m_messages.back().message = trimmed(start, names.size() - 1);
++        m_messages.back().message = static_cast<std::string>(trimmed(start, names.size() - 1));
+         m_messages.back().message += " := ";
+     }
+     Capturer::~Capturer() {
+@@ -9145,6 +11981,22 @@
+         auto str() const -> std::string;
+     };
+ 
++    class RedirectedStreams {
++    public:
++        RedirectedStreams(RedirectedStreams const&) = delete;
++        RedirectedStreams& operator=(RedirectedStreams const&) = delete;
++        RedirectedStreams(RedirectedStreams&&) = delete;
++        RedirectedStreams& operator=(RedirectedStreams&&) = delete;
++
++        RedirectedStreams(std::string& redirectedCout, std::string& redirectedCerr);
++        ~RedirectedStreams();
++    private:
++        std::string& m_redirectedCout;
++        std::string& m_redirectedCerr;
++        RedirectedStdOut m_redirectedStdOut;
++        RedirectedStdErr m_redirectedStdErr;
++    };
++
+ #if defined(CATCH_CONFIG_NEW_CAPTURE)
+ 
+     // Windows's implementation of std::tmpfile is terrible (it tries
+@@ -9236,6 +12088,16 @@
+     {}
+     auto RedirectedStdErr::str() const -> std::string { return m_rss.str(); }
+ 
++    RedirectedStreams::RedirectedStreams(std::string& redirectedCout, std::string& redirectedCerr)
++    :   m_redirectedCout(redirectedCout),
++        m_redirectedCerr(redirectedCerr)
++    {}
++
++    RedirectedStreams::~RedirectedStreams() {
++        m_redirectedCout += m_redirectedStdOut.str();
++        m_redirectedCerr += m_redirectedStdErr.str();
++    }
++
+ #if defined(CATCH_CONFIG_NEW_CAPTURE)
+ 
+ #if defined(_MSC_VER)
+@@ -9243,12 +12105,12 @@
+         if (tmpnam_s(m_buffer)) {
+             CATCH_RUNTIME_ERROR("Could not get a temp filename");
+         }
+-        if (fopen_s(&m_file, m_buffer, "w")) {
++        if (fopen_s(&m_file, m_buffer, "w+")) {
+             char buffer[100];
+             if (strerror_s(buffer, errno)) {
+                 CATCH_RUNTIME_ERROR("Could not translate errno to a string");
+             }
+-            CATCH_RUNTIME_ERROR("Coul dnot open the temp file: '" << m_buffer << "' because: " << buffer);
++            CATCH_RUNTIME_ERROR("Could not open the temp file: '" << m_buffer << "' because: " << buffer);
+         }
+     }
+ #else
+@@ -9351,20 +12213,61 @@
+ 
+ namespace Catch {
+ 
+-    std::mt19937& rng() {
+-        static std::mt19937 s_rng;
+-        return s_rng;
++namespace {
++
++#if defined(_MSC_VER)
++#pragma warning(push)
++#pragma warning(disable:4146) // we negate uint32 during the rotate
++#endif
++        // Safe rotr implementation thanks to John Regehr
++        uint32_t rotate_right(uint32_t val, uint32_t count) {
++            const uint32_t mask = 31;
++            count &= mask;
++            return (val >> count) | (val << (-count & mask));
++        }
++
++#if defined(_MSC_VER)
++#pragma warning(pop)
++#endif
++
++}
++
++    SimplePcg32::SimplePcg32(result_type seed_) {
++        seed(seed_);
++    }
++
++    void SimplePcg32::seed(result_type seed_) {
++        m_state = 0;
++        (*this)();
++        m_state += seed_;
++        (*this)();
+     }
+ 
+-    void seedRng( IConfig const& config ) {
+-        if( config.rngSeed() != 0 ) {
+-            std::srand( config.rngSeed() );
+-            rng().seed( config.rngSeed() );
++    void SimplePcg32::discard(uint64_t skip) {
++        // We could implement this to run in O(log n) steps, but this
++        // should suffice for our use case.
++        for (uint64_t s = 0; s < skip; ++s) {
++            static_cast<void>((*this)());
+         }
+     }
+ 
+-    unsigned int rngSeed() {
+-        return getCurrentContext().getConfig()->rngSeed();
++    SimplePcg32::result_type SimplePcg32::operator()() {
++        // prepare the output value
++        const uint32_t xorshifted = static_cast<uint32_t>(((m_state >> 18u) ^ m_state) >> 27u);
++        const auto output = rotate_right(xorshifted, m_state >> 59u);
++
++        // advance state
++        m_state = m_state * 6364136223846793005ULL + s_inc;
++
++        return output;
++    }
++
++    bool operator==(SimplePcg32 const& lhs, SimplePcg32 const& rhs) {
++        return lhs.m_state == rhs.m_state;
++    }
++
++    bool operator!=(SimplePcg32 const& lhs, SimplePcg32 const& rhs) {
++        return lhs.m_state != rhs.m_state;
+     }
+ }
+ // end catch_random_number_generator.cpp
+@@ -9383,6 +12286,8 @@
+     struct IConfig;
+ 
+     std::vector<TestCase> sortTests( IConfig const& config, std::vector<TestCase> const& unsortedTestCases );
++
++    bool isThrowSafe( TestCase const& testCase, IConfig const& config );
+     bool matchTest( TestCase const& testCase, TestSpec const& testSpec, IConfig const& config );
+ 
+     void enforceNoDuplicateTestCases( std::vector<TestCase> const& functions );
+@@ -9495,11 +12400,13 @@
+ namespace Catch {
+ 
+     class StartupExceptionRegistry {
++#if !defined(CATCH_CONFIG_DISABLE_EXCEPTIONS)
+     public:
+         void add(std::exception_ptr const& exception) noexcept;
+         std::vector<std::exception_ptr> const& getExceptions() const noexcept;
+     private:
+         std::vector<std::exception_ptr> m_exceptions;
++#endif
+     };
+ 
+ } // end namespace Catch
+@@ -9582,7 +12489,14 @@
+                 m_tagAliasRegistry.add( alias, tag, lineInfo );
+             }
+             void registerStartupException() noexcept override {
++#if !defined(CATCH_CONFIG_DISABLE_EXCEPTIONS)
+                 m_exceptionRegistry.add(std::current_exception());
++#else
++                CATCH_INTERNAL_ERROR("Attempted to register active exception under CATCH_CONFIG_DISABLE_EXCEPTIONS!");
++#endif
++            }
++            IMutableEnumValuesRegistry& getMutableEnumValuesRegistry() override {
++                return m_enumValuesRegistry;
+             }
+ 
+         private:
+@@ -9591,6 +12505,7 @@
+             ExceptionTranslatorRegistry m_exceptionTranslatorRegistry;
+             TagAliasRegistry m_tagAliasRegistry;
+             StartupExceptionRegistry m_exceptionRegistry;
++            Detail::EnumValuesRegistry m_enumValuesRegistry;
+         };
+     }
+ 
+@@ -9671,7 +12586,6 @@
+ 
+     namespace Generators {
+         struct GeneratorTracker : TestCaseTracking::TrackerBase, IGeneratorTracker {
+-            size_t m_index = static_cast<size_t>( -1 );
+             GeneratorBasePtr m_generator;
+ 
+             GeneratorTracker( TestCaseTracking::NameAndLocation const& nameAndLocation, TrackerContext& ctx, ITracker* parent )
+@@ -9683,39 +12597,110 @@
+                 std::shared_ptr<GeneratorTracker> tracker;
+ 
+                 ITracker& currentTracker = ctx.currentTracker();
+-                if( TestCaseTracking::ITrackerPtr childTracker = currentTracker.findChild( nameAndLocation ) ) {
++                // Under specific circumstances, the generator we want
++                // to acquire is also the current tracker. If this is
++                // the case, we have to avoid looking through current
++                // tracker's children, and instead return the current
++                // tracker.
++                // A case where this check is important is e.g.
++                //     for (int i = 0; i < 5; ++i) {
++                //         int n = GENERATE(1, 2);
++                //     }
++                //
++                // without it, the code above creates 5 nested generators.
++                if (currentTracker.nameAndLocation() == nameAndLocation) {
++                    auto thisTracker = currentTracker.parent().findChild(nameAndLocation);
++                    assert(thisTracker);
++                    assert(thisTracker->isGeneratorTracker());
++                    tracker = std::static_pointer_cast<GeneratorTracker>(thisTracker);
++                } else if ( TestCaseTracking::ITrackerPtr childTracker = currentTracker.findChild( nameAndLocation ) ) {
+                     assert( childTracker );
+-                    assert( childTracker->isIndexTracker() );
++                    assert( childTracker->isGeneratorTracker() );
+                     tracker = std::static_pointer_cast<GeneratorTracker>( childTracker );
+-                }
+-                else {
++                } else {
+                     tracker = std::make_shared<GeneratorTracker>( nameAndLocation, ctx, &currentTracker );
+                     currentTracker.addChild( tracker );
+                 }
+ 
+-                if( !ctx.completedCycle() && !tracker->isComplete() ) {
+-                    if( tracker->m_runState != ExecutingChildren && tracker->m_runState != NeedsAnotherRun )
+-                        tracker->moveNext();
++                if( !tracker->isComplete() ) {
+                     tracker->open();
+                 }
+ 
+                 return *tracker;
+             }
+ 
+-            void moveNext() {
+-                m_index++;
+-                m_children.clear();
+-            }
+-
+             // TrackerBase interface
+-            bool isIndexTracker() const override { return true; }
++            bool isGeneratorTracker() const override { return true; }
+             auto hasGenerator() const -> bool override {
+                 return !!m_generator;
+             }
+             void close() override {
+                 TrackerBase::close();
+-                if( m_runState == CompletedSuccessfully && m_index < m_generator->size()-1 )
++                // If a generator has a child (it is followed by a section)
++                // and none of its children have started, then we must wait
++                // until later to start consuming its values.
++                // This catches cases where `GENERATE` is placed between two
++                // `SECTION`s.
++                // **The check for m_children.empty cannot be removed**.
++                // doing so would break `GENERATE` _not_ followed by `SECTION`s.
++                const bool should_wait_for_child = [&]() {
++                    // No children -> nobody to wait for
++                    if ( m_children.empty() ) {
++                        return false;
++                    }
++                    // If at least one child started executing, don't wait
++                    if ( std::find_if(
++                             m_children.begin(),
++                             m_children.end(),
++                             []( TestCaseTracking::ITrackerPtr tracker ) {
++                                 return tracker->hasStarted();
++                             } ) != m_children.end() ) {
++                        return false;
++                    }
++
++                    // No children have started. We need to check if they _can_
++                    // start, and thus we should wait for them, or they cannot
++                    // start (due to filters), and we shouldn't wait for them
++                    auto* parent = m_parent;
++                    // This is safe: there is always at least one section
++                    // tracker in a test case tracking tree
++                    while ( !parent->isSectionTracker() ) {
++                        parent = &( parent->parent() );
++                    }
++                    assert( parent &&
++                            "Missing root (test case) level section" );
++
++                    auto const& parentSection =
++                        static_cast<SectionTracker&>( *parent );
++                    auto const& filters = parentSection.getFilters();
++                    // No filters -> no restrictions on running sections
++                    if ( filters.empty() ) {
++                        return true;
++                    }
++
++                    for ( auto const& child : m_children ) {
++                        if ( child->isSectionTracker() &&
++                             std::find( filters.begin(),
++                                        filters.end(),
++                                        static_cast<SectionTracker&>( *child )
++                                            .trimmedName() ) !=
++                                 filters.end() ) {
++                            return true;
++                        }
++                    }
++                    return false;
++                }();
++
++                // This check is a bit tricky, because m_generator->next()
++                // has a side-effect, where it consumes generator's current
++                // value, but we do not want to invoke the side-effect if
++                // this generator is still waiting for any child to start.
++                if ( should_wait_for_child ||
++                     ( m_runState == CompletedSuccessfully &&
++                       m_generator->next() ) ) {
++                    m_children.clear();
+                     m_runState = Executing;
++                }
+             }
+ 
+             // IGeneratorTracker interface
+@@ -9725,9 +12710,6 @@
+             void setGenerator( GeneratorBasePtr&& generator ) override {
+                 m_generator = std::move( generator );
+             }
+-            auto getIndex() const -> size_t override {
+-                return m_index;
+-            }
+         };
+         GeneratorTracker::~GeneratorTracker() {}
+     }
+@@ -9825,6 +12807,9 @@
+         // and should be let to clear themselves out.
+         static_cast<void>(m_reporter->assertionEnded(AssertionStats(result, m_messages, m_totals)));
+ 
++        if (result.getResultType() != ResultWas::Warning)
++            m_messageScopes.clear();
++
+         // Reset working state
+         resetAssertionInfo();
+         m_lastResult = result;
+@@ -9848,10 +12833,10 @@
+ 
+         return true;
+     }
+-    auto RunContext::acquireGeneratorTracker( SourceLineInfo const& lineInfo ) -> IGeneratorTracker& {
++    auto RunContext::acquireGeneratorTracker( StringRef generatorName, SourceLineInfo const& lineInfo ) -> IGeneratorTracker& {
+         using namespace Generators;
+-        GeneratorTracker& tracker = GeneratorTracker::acquire( m_trackerContext, TestCaseTracking::NameAndLocation( "generator", lineInfo ) );
+-        assert( tracker.isOpen() );
++        GeneratorTracker& tracker = GeneratorTracker::acquire(m_trackerContext,
++                                                              TestCaseTracking::NameAndLocation( static_cast<std::string>(generatorName), lineInfo ) );
+         m_lastAssertionInfo.lineInfo = lineInfo;
+         return tracker;
+     }
+@@ -9879,6 +12864,7 @@
+ 
+         m_reporter->sectionEnded(SectionStats(endInfo.sectionInfo, assertions, endInfo.durationInSeconds, missingAssertions));
+         m_messages.clear();
++        m_messageScopes.clear();
+     }
+ 
+     void RunContext::sectionEndedEarly(SectionEndInfo const & endInfo) {
+@@ -9890,12 +12876,21 @@
+ 
+         m_unfinishedSections.push_back(endInfo);
+     }
++
++#if defined(CATCH_CONFIG_ENABLE_BENCHMARKING)
++    void RunContext::benchmarkPreparing(std::string const& name) {
++        m_reporter->benchmarkPreparing(name);
++    }
+     void RunContext::benchmarkStarting( BenchmarkInfo const& info ) {
+         m_reporter->benchmarkStarting( info );
+     }
+-    void RunContext::benchmarkEnded( BenchmarkStats const& stats ) {
++    void RunContext::benchmarkEnded( BenchmarkStats<> const& stats ) {
+         m_reporter->benchmarkEnded( stats );
+     }
++    void RunContext::benchmarkFailed(std::string const & error) {
++        m_reporter->benchmarkFailed(error);
++    }
++#endif // CATCH_CONFIG_ENABLE_BENCHMARKING
+ 
+     void RunContext::pushScopedMessage(MessageInfo const & message) {
+         m_messages.push_back(message);
+@@ -9905,6 +12900,10 @@
+         m_messages.erase(std::remove(m_messages.begin(), m_messages.end(), message), m_messages.end());
+     }
+ 
++    void RunContext::emplaceUnscopedMessage( MessageBuilder const& builder ) {
++        m_messageScopes.emplace_back( builder );
++    }
++
+     std::string RunContext::getCurrentTestName() const {
+         return m_activeTestCase
+             ? m_activeTestCase->getTestCaseInfo().name
+@@ -9926,7 +12925,7 @@
+         // Don't rebuild the result -- the stringification itself can cause more fatal errors
+         // Instead, fake a result data.
+         AssertionResultData tempResult( ResultWas::FatalErrorCondition, { false } );
+-        tempResult.message = message;
++        tempResult.message = static_cast<std::string>(message);
+         AssertionResult result(m_lastAssertionInfo, tempResult);
+ 
+         assertionEnded(result);
+@@ -9965,6 +12964,7 @@
+         m_lastAssertionPassed = true;
+         ++m_totals.assertions.passed;
+         resetAssertionInfo();
++        m_messageScopes.clear();
+     }
+ 
+     bool RunContext::aborting() const {
+@@ -9986,13 +12986,10 @@
+         CATCH_TRY {
+             if (m_reporter->getPreferences().shouldRedirectStdOut) {
+ #if !defined(CATCH_CONFIG_EXPERIMENTAL_REDIRECT)
+-                RedirectedStdOut redirectedStdOut;
+-                RedirectedStdErr redirectedStdErr;
++                RedirectedStreams redirectedStreams(redirectedCout, redirectedCerr);
+ 
+                 timer.start();
+                 invokeActiveTestCase();
+-                redirectedCout += redirectedStdOut.str();
+-                redirectedCerr += redirectedStdErr.str();
+ #else
+                 OutputRedirect r(redirectedCout, redirectedCerr);
+                 timer.start();
+@@ -10019,15 +13016,15 @@
+         m_testCaseTracker->close();
+         handleUnfinishedSections();
+         m_messages.clear();
++        m_messageScopes.clear();
+ 
+         SectionStats testCaseSectionStats(testCaseSection, assertions, duration, missingAssertions);
+         m_reporter->sectionEnded(testCaseSectionStats);
+     }
+ 
+     void RunContext::invokeActiveTestCase() {
+-        FatalConditionHandler fatalConditionHandler; // Handle signals
++        FatalConditionHandlerGuard _(&m_fatalConditionhandler);
+         m_activeTestCase->invoke();
+-        fatalConditionHandler.reset();
+     }
+ 
+     void RunContext::handleUnfinishedSections() {
+@@ -10090,7 +13087,7 @@
+         m_lastAssertionInfo = info;
+ 
+         AssertionResultData data( resultType, LazyExpression( false ) );
+-        data.message = message;
++        data.message = static_cast<std::string>(message);
+         AssertionResult assertionResult{ m_lastAssertionInfo, data };
+         assertionEnded( assertionResult );
+         if( !assertionResult.isOk() )
+@@ -10153,6 +13150,18 @@
+         else
+             CATCH_INTERNAL_ERROR("No result capture instance");
+     }
++
++    void seedRng(IConfig const& config) {
++        if (config.rngSeed() != 0) {
++            std::srand(config.rngSeed());
++            rng().seed(config.rngSeed());
++        }
++    }
++
++    unsigned int rngSeed() {
++        return getCurrentContext().getConfig()->rngSeed();
++    }
++
+ }
+ // end catch_run_context.cpp
+ // start catch_section.cpp
+@@ -10214,7 +13223,7 @@
+         void libIdentify();
+ 
+         int applyCommandLine( int argc, char const * const * argv );
+-    #if defined(CATCH_CONFIG_WCHAR) && defined(WIN32) && defined(UNICODE)
++    #if defined(CATCH_CONFIG_WCHAR) && defined(_WIN32) && defined(UNICODE)
+         int applyCommandLine( int argc, wchar_t const * const * argv );
+     #endif
+ 
+@@ -10281,6 +13290,8 @@
+ // end catch_version.h
+ #include <cstdlib>
+ #include <iomanip>
++#include <set>
++#include <iterator>
+ 
+ namespace Catch {
+ 
+@@ -10299,52 +13310,76 @@
+                 return createReporter(config->getReporterName(), config);
+             }
+ 
+-            auto multi = std::unique_ptr<ListeningReporter>(new ListeningReporter);
+-
++            // On older platforms, returning std::unique_ptr<ListeningReporter>
++            // when the return type is std::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 = std::unique_ptr<IStreamingReporter>(new ListeningReporter);
++            auto& multi = static_cast<ListeningReporter&>(*ret);
+             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 std::move(multi);
++            multi.addReporter(createReporter(config->getReporterName(), config));
++            return ret;
+         }
+ 
+-        Catch::Totals runTests(std::shared_ptr<Config> const& config) {
+-            auto reporter = makeReporter(config);
+-
+-            RunContext context(config, std::move(reporter));
+-
+-            Totals totals;
+-
+-            context.testGroupStarting(config->name(), 1, 1);
+-
+-            TestSpec testSpec = config->testSpec();
+-
+-            auto const& allTestCases = getAllTestCasesSorted(*config);
+-            for (auto const& testCase : allTestCases) {
+-                if (!context.aborting() && matchTest(testCase, testSpec, *config))
+-                    totals += context.runTest(testCase);
+-                else
+-                    context.reporter().skipTest(testCase);
++        class TestGroup {
++        public:
++            explicit TestGroup(std::shared_ptr<Config> const& config)
++            : m_config{config}
++            , m_context{config, makeReporter(config)}
++            {
++                auto const& allTestCases = getAllTestCasesSorted(*m_config);
++                m_matches = m_config->testSpec().matchesByFilter(allTestCases, *m_config);
++                auto const& invalidArgs = m_config->testSpec().getInvalidArgs();
++
++                if (m_matches.empty() && invalidArgs.empty()) {
++                    for (auto const& test : allTestCases)
++                        if (!test.isHidden())
++                            m_tests.emplace(&test);
++                } else {
++                    for (auto const& match : m_matches)
++                        m_tests.insert(match.tests.begin(), match.tests.end());
++                }
+             }
+ 
+-            if (config->warnAboutNoTests() && totals.testCases.total() == 0) {
+-                ReusableStringStream testConfig;
++            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);
++                    else
++                        m_context.reporter().skipTest(*testCase);
++                }
+ 
+-                bool first = true;
+-                for (const auto& input : config->getTestsOrTags()) {
+-                    if (!first) { testConfig << ' '; }
+-                    first = false;
+-                    testConfig << input;
++                for (auto const& match : m_matches) {
++                    if (match.tests.empty()) {
++                        m_context.reporter().noMatchingTestCases(match.name);
++                        totals.error = -1;
++                    }
+                 }
+ 
+-                context.reporter().noMatchingTestCases(testConfig.str());
+-                totals.error = -1;
++                if (!invalidArgs.empty()) {
++                    for (auto const& invalidArg: invalidArgs)
++                         m_context.reporter().reportInvalidArguments(invalidArg);
++                }
++
++                m_context.testGroupEnded(m_config->name(), totals, 1, 1);
++                return totals;
+             }
+ 
+-            context.testGroupEnded(config->name(), totals, 1, 1);
+-            return totals;
+-        }
++        private:
++            using Tests = std::set<TestCase const*>;
++
++            std::shared_ptr<Config> m_config;
++            RunContext m_context;
++            Tests m_tests;
++            TestSpec::Matches m_matches;
++        };
+ 
+         void applyFilenamesAsTags(Catch::IConfig const& config) {
+             auto& tests = const_cast<std::vector<TestCase>&>(getAllTestCasesSorted(config));
+@@ -10381,6 +13416,9 @@
+ #if !defined(CATCH_CONFIG_DISABLE_EXCEPTIONS)
+         const auto& exceptions = getRegistryHub().getStartupExceptionRegistry().getExceptions();
+         if ( !exceptions.empty() ) {
++            config();
++            getCurrentMutableContext().setConfig(m_config);
++
+             m_startupExceptions = true;
+             Colour colourGuard( Colour::Red );
+             Catch::cerr() << "Errors occurred during startup!" << '\n';
+@@ -10410,7 +13448,7 @@
+     }
+     void Session::libIdentify() {
+         Catch::cout()
+-                << std::left << std::setw(16) << "description: " << "A Catch test executable\n"
++                << 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;
+@@ -10422,6 +13460,8 @@
+ 
+         auto result = m_cli.parse( clara::Args( argc, argv ) );
+         if( !result ) {
++            config();
++            getCurrentMutableContext().setConfig(m_config);
+             Catch::cerr()
+                 << Colour( Colour::Red )
+                 << "\nError(s) in input:\n"
+@@ -10439,17 +13479,17 @@
+         return 0;
+     }
+ 
+-#if defined(CATCH_CONFIG_WCHAR) && defined(WIN32) && defined(UNICODE)
++#if defined(CATCH_CONFIG_WCHAR) && defined(_WIN32) && defined(UNICODE)
+     int Session::applyCommandLine( int argc, wchar_t const * const * argv ) {
+ 
+         char **utf8Argv = new char *[ argc ];
+ 
+         for ( int i = 0; i < argc; ++i ) {
+-            int bufSize = WideCharToMultiByte( CP_UTF8, 0, argv[i], -1, NULL, 0, NULL, NULL );
++            int bufSize = WideCharToMultiByte( CP_UTF8, 0, argv[i], -1, nullptr, 0, nullptr, nullptr );
+ 
+             utf8Argv[ i ] = new char[ bufSize ];
+ 
+-            WideCharToMultiByte( CP_UTF8, 0, argv[i], -1, utf8Argv[i], bufSize, NULL, NULL );
++            WideCharToMultiByte( CP_UTF8, 0, argv[i], -1, utf8Argv[i], bufSize, nullptr, nullptr );
+         }
+ 
+         int returnCode = applyCommandLine( argc, utf8Argv );
+@@ -10513,10 +13553,15 @@
+                 applyFilenamesAsTags( *m_config );
+ 
+             // Handle list request
+-            if( Option<std::size_t> listed = list( config() ) )
++            if( Option<std::size_t> listed = list( m_config ) )
+                 return static_cast<int>( *listed );
+ 
+-            auto totals = runTests( m_config );
++            TestGroup tests { m_config };
++            auto const totals = tests.execute();
++
++            if( m_config->warnAboutNoTests() && totals.error == -1 )
++                return 2;
++
+             // Note that on unices only the lower 8 bits are usually used, clamping
+             // the return value to 255 prevents false negative when some multiple
+             // of 256 tests has failed
+@@ -10564,6 +13609,7 @@
+ // end catch_singletons.cpp
+ // start catch_startup_exception_registry.cpp
+ 
++#if !defined(CATCH_CONFIG_DISABLE_EXCEPTIONS)
+ namespace Catch {
+ void StartupExceptionRegistry::add( std::exception_ptr const& exception ) noexcept {
+         CATCH_TRY {
+@@ -10579,6 +13625,7 @@
+     }
+ 
+ } // end namespace Catch
++#endif
+ // end catch_startup_exception_registry.cpp
+ // start catch_stream.cpp
+ 
+@@ -10593,7 +13640,7 @@
+ 
+     Catch::IStream::~IStream() = default;
+ 
+-    namespace detail { namespace {
++    namespace Detail { namespace {
+         template<typename WriterF, std::size_t bufferSize=256>
+         class StreamBufImpl : public std::streambuf {
+             char data[bufferSize];
+@@ -10692,15 +13739,15 @@
+ 
+     auto makeStream( StringRef const &filename ) -> IStream const* {
+         if( filename.empty() )
+-            return new detail::CoutStream();
++            return new Detail::CoutStream();
+         else if( filename[0] == '%' ) {
+             if( filename == "%debug" )
+-                return new detail::DebugOutStream();
++                return new Detail::DebugOutStream();
+             else
+                 CATCH_ERROR( "Unrecognised stream: '" << filename << "'" );
+         }
+         else
+-            return new detail::FileStream( filename );
++            return new Detail::FileStream( filename );
+     }
+ 
+     // This class encapsulates the idea of a pool of ostringstreams that can be reused.
+@@ -10757,12 +13804,13 @@
+ #include <ostream>
+ #include <cstring>
+ #include <cctype>
++#include <vector>
+ 
+ namespace Catch {
+ 
+     namespace {
+         char toLowerCh(char c) {
+-            return static_cast<char>( std::tolower( c ) );
++            return static_cast<char>( std::tolower( static_cast<unsigned char>(c) ) );
+         }
+     }
+ 
+@@ -10797,6 +13845,18 @@
+         return start != std::string::npos ? str.substr( start, 1+end-start ) : std::string();
+     }
+ 
++    StringRef trim(StringRef ref) {
++        const auto is_ws = [](char c) {
++            return c == ' ' || c == '\t' || c == '\n' || c == '\r';
++        };
++        size_t real_begin = 0;
++        while (real_begin < ref.size() && is_ws(ref[real_begin])) { ++real_begin; }
++        size_t real_end = ref.size();
++        while (real_end > real_begin && is_ws(ref[real_end - 1])) { --real_end; }
++
++        return ref.substr(real_begin, real_end - real_begin);
++    }
++
+     bool replaceInPlace( std::string& str, std::string const& replaceThis, std::string const& withThis ) {
+         bool replaced = false;
+         std::size_t i = str.find( replaceThis );
+@@ -10811,6 +13871,21 @@
+         return replaced;
+     }
+ 
++    std::vector<StringRef> splitStringRef( StringRef str, char delimiter ) {
++        std::vector<StringRef> subStrings;
++        std::size_t start = 0;
++        for(std::size_t pos = 0; pos < str.size(); ++pos ) {
++            if( str[pos] == delimiter ) {
++                if( pos - start > 1 )
++                    subStrings.push_back( str.substr( start, pos-start ) );
++                start = pos+1;
++            }
++        }
++        if( start < str.size() )
++            subStrings.push_back( str.substr( start, str.size()-start ) );
++        return subStrings;
++    }
++
+     pluralise::pluralise( std::size_t count, std::string const& label )
+     :   m_count( count ),
+         m_label( label )
+@@ -10827,123 +13902,46 @@
+ // end catch_string_manip.cpp
+ // start catch_stringref.cpp
+ 
+-#if defined(__clang__)
+-#    pragma clang diagnostic push
+-#    pragma clang diagnostic ignored "-Wexit-time-destructors"
+-#endif
+-
++#include <algorithm>
+ #include <ostream>
+ #include <cstring>
+ #include <cstdint>
+ 
+-namespace {
+-    const uint32_t byte_2_lead = 0xC0;
+-    const uint32_t byte_3_lead = 0xE0;
+-    const uint32_t byte_4_lead = 0xF0;
+-}
+-
+ namespace Catch {
+     StringRef::StringRef( char const* rawChars ) noexcept
+     : StringRef( rawChars, static_cast<StringRef::size_type>(std::strlen(rawChars) ) )
+     {}
+ 
+-    StringRef::operator std::string() const {
+-        return std::string( m_start, m_size );
+-    }
+-
+-    void StringRef::swap( StringRef& other ) noexcept {
+-        std::swap( m_start, other.m_start );
+-        std::swap( m_size, other.m_size );
+-        std::swap( m_data, other.m_data );
+-    }
+-
+     auto StringRef::c_str() const -> char const* {
+-        if( isSubstring() )
+-           const_cast<StringRef*>( this )->takeOwnership();
++        CATCH_ENFORCE(isNullTerminated(), "Called StringRef::c_str() on a non-null-terminated instance");
+         return m_start;
+     }
+-    auto StringRef::currentData() const noexcept -> char const* {
++    auto StringRef::data() const noexcept -> char const* {
+         return m_start;
+     }
+ 
+-    auto StringRef::isOwned() const noexcept -> bool {
+-        return m_data != nullptr;
+-    }
+-    auto StringRef::isSubstring() const noexcept -> bool {
+-        return m_start[m_size] != '\0';
+-    }
+-
+-    void StringRef::takeOwnership() {
+-        if( !isOwned() ) {
+-            m_data = new char[m_size+1];
+-            memcpy( m_data, m_start, m_size );
+-            m_data[m_size] = '\0';
+-            m_start = m_data;
+-        }
+-    }
+     auto StringRef::substr( size_type start, size_type size ) const noexcept -> StringRef {
+-        if( start < m_size )
+-            return StringRef( m_start+start, size );
+-        else
++        if (start < m_size) {
++            return StringRef(m_start + start, (std::min)(m_size - start, size));
++        } else {
+             return StringRef();
+-    }
+-    auto StringRef::operator == ( StringRef const& other ) const noexcept -> bool {
+-        return
+-            size() == other.size() &&
+-            (std::strncmp( m_start, other.m_start, size() ) == 0);
+-    }
+-    auto StringRef::operator != ( StringRef const& other ) const noexcept -> bool {
+-        return !operator==( other );
+-    }
+-
+-    auto StringRef::operator[](size_type index) const noexcept -> char {
+-        return m_start[index];
+-    }
+-
+-    auto StringRef::numberOfCharacters() const noexcept -> size_type {
+-        size_type noChars = m_size;
+-        // Make adjustments for uft encodings
+-        for( size_type i=0; i < m_size; ++i ) {
+-            char c = m_start[i];
+-            if( ( c & byte_2_lead ) == byte_2_lead ) {
+-                noChars--;
+-                if (( c & byte_3_lead ) == byte_3_lead )
+-                    noChars--;
+-                if( ( c & byte_4_lead ) == byte_4_lead )
+-                    noChars--;
+-            }
+         }
+-        return noChars;
+     }
+-
+-    auto operator + ( StringRef const& lhs, StringRef const& rhs ) -> std::string {
+-        std::string str;
+-        str.reserve( lhs.size() + rhs.size() );
+-        str += lhs;
+-        str += rhs;
+-        return str;
+-    }
+-    auto operator + ( StringRef const& lhs, const char* rhs ) -> std::string {
+-        return std::string( lhs ) + std::string( rhs );
+-    }
+-    auto operator + ( char const* lhs, StringRef const& rhs ) -> std::string {
+-        return std::string( lhs ) + std::string( rhs );
++    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);
+     }
+ 
+     auto operator << ( std::ostream& os, StringRef const& str ) -> std::ostream& {
+-        return os.write(str.currentData(), str.size());
++        return os.write(str.data(), str.size());
+     }
+ 
+     auto operator+=( std::string& lhs, StringRef const& rhs ) -> std::string& {
+-        lhs.append(rhs.currentData(), rhs.size());
++        lhs.append(rhs.data(), rhs.size());
+         return lhs;
+     }
+ 
+ } // namespace Catch
+-
+-#if defined(__clang__)
+-#    pragma clang diagnostic pop
+-#endif
+ // end catch_stringref.cpp
+ // start catch_tag_alias.cpp
+ 
+@@ -11046,7 +14044,7 @@
+         void enforceNotReservedTag( std::string const& tag, SourceLineInfo const& _lineInfo ) {
+             CATCH_ENFORCE( !isReservedTag(tag),
+                           "Tag name: [" << tag << "] is not allowed.\n"
+-                          << "Tag names starting with non alpha-numeric characters are reserved\n"
++                          << "Tag names starting with non alphanumeric characters are reserved\n"
+                           << _lineInfo );
+         }
+     }
+@@ -11062,8 +14060,7 @@
+         std::vector<std::string> tags;
+         std::string desc, tag;
+         bool inTag = false;
+-        std::string _descOrTags = nameAndTags.tags;
+-        for (char c : _descOrTags) {
++        for (char c : nameAndTags.tags) {
+             if( !inTag ) {
+                 if( c == '[' )
+                     inTag = true;
+@@ -11078,6 +14075,12 @@
+                     else if( prop == TestCaseInfo::None )
+                         enforceNotReservedTag( tag, _lineInfo );
+ 
++                    // Merged hide tags like `[.approvals]` should be added as
++                    // `[.][approvals]`. The `[.]` is added at later point, so
++                    // we only strip the prefix
++                    if (startsWith(tag, '.') && tag.size() > 1) {
++                        tag.erase(0, 1);
++                    }
+                     tags.push_back( tag );
+                     tag.clear();
+                     inTag = false;
+@@ -11087,10 +14090,11 @@
+             }
+         }
+         if( isHidden ) {
+-            tags.push_back( "." );
++            // Add all "hidden" tags to make them behave identically
++            tags.insert( tags.end(), { ".", "!hide" } );
+         }
+ 
+-        TestCaseInfo info( nameAndTags.name, _className, desc, tags, _lineInfo );
++        TestCaseInfo info( static_cast<std::string>(nameAndTags.name), _className, desc, tags, _lineInfo );
+         return TestCase( _testCase, std::move(info) );
+     }
+ 
+@@ -11182,30 +14186,89 @@
+ // end catch_test_case_info.cpp
+ // start catch_test_case_registry_impl.cpp
+ 
++#include <algorithm>
+ #include <sstream>
+ 
+ namespace Catch {
+ 
+-    std::vector<TestCase> sortTests( IConfig const& config, std::vector<TestCase> const& unsortedTestCases ) {
++    namespace {
++        struct TestHasher {
++            using hash_t = uint64_t;
+ 
+-        std::vector<TestCase> sorted = unsortedTestCases;
++            explicit TestHasher( hash_t hashSuffix ):
++                m_hashSuffix{ hashSuffix } {}
++
++            uint32_t operator()( TestCase 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;
++                }
++                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;
++            }
++
++        private:
++            hash_t m_hashSuffix;
++        };
++    } // end unnamed namespace
+ 
++    std::vector<TestCase> sortTests( IConfig const& config, std::vector<TestCase> const& unsortedTestCases ) {
+         switch( config.runOrder() ) {
+-            case RunTests::InLexicographicalOrder:
+-                std::sort( sorted.begin(), sorted.end() );
+-                break;
+-            case RunTests::InRandomOrder:
+-                seedRng( config );
+-                std::shuffle( sorted.begin(), sorted.end(), rng() );
+-                break;
+             case RunTests::InDeclarationOrder:
+                 // already in declaration order
+                 break;
++
++            case RunTests::InLexicographicalOrder: {
++                std::vector<TestCase> sorted = unsortedTestCases;
++                std::sort( sorted.begin(), sorted.end() );
++                return sorted;
++            }
++
++            case RunTests::InRandomOrder: {
++                seedRng( config );
++                TestHasher h{ config.rngSeed() };
++
++                using hashedTest = std::pair<TestHasher::hash_t, TestCase const*>;
++                std::vector<hashedTest> indexed_tests;
++                indexed_tests.reserve( unsortedTestCases.size() );
++
++                for (auto const& testCase : unsortedTestCases) {
++                    indexed_tests.emplace_back(h(testCase), &testCase);
++                }
++
++                std::sort(indexed_tests.begin(), indexed_tests.end(),
++                          [](hashedTest const& lhs, hashedTest const& rhs) {
++                          if (lhs.first == rhs.first) {
++                              return lhs.second->name < rhs.second->name;
++                          }
++                          return lhs.first < rhs.first;
++                });
++
++                std::vector<TestCase> sorted;
++                sorted.reserve( indexed_tests.size() );
++
++                for (auto const& hashed : indexed_tests) {
++                    sorted.emplace_back(*hashed.second);
++                }
++
++                return sorted;
++            }
+         }
+-        return sorted;
++        return unsortedTestCases;
++    }
++
++    bool isThrowSafe( TestCase const& testCase, IConfig const& config ) {
++        return !testCase.throws() || config.allowThrows();
+     }
++
+     bool matchTest( TestCase const& testCase, TestSpec const& testSpec, IConfig const& config ) {
+-        return testSpec.matches( testCase ) && ( config.allowThrows() || !testCase.throws() );
++        return testSpec.matches( testCase ) && isThrowSafe( testCase, config );
+     }
+ 
+     void enforceNoDuplicateTestCases( std::vector<TestCase> const& functions ) {
+@@ -11222,9 +14285,12 @@
+     std::vector<TestCase> filterTests( std::vector<TestCase> const& testCases, TestSpec const& testSpec, IConfig const& config ) {
+         std::vector<TestCase> filtered;
+         filtered.reserve( testCases.size() );
+-        for( auto const& testCase : testCases )
+-            if( matchTest( testCase, testSpec, config ) )
+-                filtered.push_back( testCase );
++        for (auto const& testCase : testCases) {
++            if ((!testSpec.hasFilters() && !testCase.isHidden()) ||
++                (testSpec.hasFilters() && matchTest(testCase, testSpec, config))) {
++                filtered.push_back(testCase);
++            }
++        }
+         return filtered;
+     }
+     std::vector<TestCase> const& getAllTestCasesSorted( IConfig const& config ) {
+@@ -11263,7 +14329,7 @@
+     }
+ 
+     std::string extractClassName( StringRef const& classOrQualifiedMethodName ) {
+-        std::string className = classOrQualifiedMethodName;
++        std::string className(classOrQualifiedMethodName);
+         if( startsWith( className, '&' ) )
+         {
+             std::size_t lastColons = className.rfind( "::" );
+@@ -11300,11 +14366,6 @@
+ 
+     ITracker::~ITracker() = default;
+ 
+-    TrackerContext& TrackerContext::instance() {
+-        static TrackerContext s_instance;
+-        return s_instance;
+-    }
+-
+     ITracker& TrackerContext::startRun() {
+         m_rootTracker = std::make_shared<SectionTracker>( NameAndLocation( "{root}", CATCH_INTERNAL_LINEINFO ), *this, nullptr );
+         m_currentTracker = nullptr;
+@@ -11336,15 +14397,12 @@
+         m_currentTracker = tracker;
+     }
+ 
+-    TrackerBase::TrackerBase( NameAndLocation const& nameAndLocation, TrackerContext& ctx, ITracker* parent )
+-    :   m_nameAndLocation( nameAndLocation ),
++    TrackerBase::TrackerBase( NameAndLocation const& nameAndLocation, TrackerContext& ctx, ITracker* parent ):
++        ITracker(nameAndLocation),
+         m_ctx( ctx ),
+         m_parent( parent )
+     {}
+ 
+-    NameAndLocation const& TrackerBase::nameAndLocation() const {
+-        return m_nameAndLocation;
+-    }
+     bool TrackerBase::isComplete() const {
+         return m_runState == CompletedSuccessfully || m_runState == Failed;
+     }
+@@ -11387,7 +14445,7 @@
+     }
+ 
+     bool TrackerBase::isSectionTracker() const { return false; }
+-    bool TrackerBase::isIndexTracker() const { return false; }
++    bool TrackerBase::isGeneratorTracker() const { return false; }
+ 
+     void TrackerBase::open() {
+         m_runState = Executing;
+@@ -11410,7 +14468,7 @@
+                 m_runState = CompletedSuccessfully;
+                 break;
+             case ExecutingChildren:
+-                if( m_children.empty() || m_children.back()->isComplete() )
++                if( std::all_of(m_children.begin(), m_children.end(), [](ITrackerPtr const& t){ return t->isComplete(); }) )
+                     m_runState = CompletedSuccessfully;
+                 break;
+ 
+@@ -11445,7 +14503,8 @@
+     }
+ 
+     SectionTracker::SectionTracker( NameAndLocation const& nameAndLocation, TrackerContext& ctx, ITracker* parent )
+-    :   TrackerBase( nameAndLocation, ctx, parent )
++    :   TrackerBase( nameAndLocation, ctx, parent ),
++        m_trimmed_name(trim(nameAndLocation.name))
+     {
+         if( parent ) {
+             while( !parent->isSectionTracker() )
+@@ -11456,6 +14515,17 @@
+         }
+     }
+ 
++    bool SectionTracker::isComplete() const {
++        bool complete = true;
++
++        if (m_filters.empty()
++            || m_filters[0] == ""
++            || std::find(m_filters.begin(), m_filters.end(), m_trimmed_name) != m_filters.end()) {
++            complete = TrackerBase::isComplete();
++        }
++        return complete;
++    }
++
+     bool SectionTracker::isSectionTracker() const { return true; }
+ 
+     SectionTracker& SectionTracker::acquire( TrackerContext& ctx, NameAndLocation const& nameAndLocation ) {
+@@ -11477,63 +14547,29 @@
+     }
+ 
+     void SectionTracker::tryOpen() {
+-        if( !isComplete() && (m_filters.empty() || m_filters[0].empty() ||  m_filters[0] == m_nameAndLocation.name ) )
++        if( !isComplete() )
+             open();
+     }
+ 
+     void SectionTracker::addInitialFilters( std::vector<std::string> const& filters ) {
+         if( !filters.empty() ) {
+-            m_filters.push_back(""); // Root - should never be consulted
+-            m_filters.push_back(""); // Test Case - not a section filter
++            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.insert( m_filters.end(), filters.begin(), filters.end() );
+         }
+     }
+     void SectionTracker::addNextFilters( std::vector<std::string> const& filters ) {
+         if( filters.size() > 1 )
+-            m_filters.insert( m_filters.end(), ++filters.begin(), filters.end() );
+-    }
+-
+-    IndexTracker::IndexTracker( NameAndLocation const& nameAndLocation, TrackerContext& ctx, ITracker* parent, int size )
+-    :   TrackerBase( nameAndLocation, ctx, parent ),
+-        m_size( size )
+-    {}
+-
+-    bool IndexTracker::isIndexTracker() const { return true; }
+-
+-    IndexTracker& IndexTracker::acquire( TrackerContext& ctx, NameAndLocation const& nameAndLocation, int size ) {
+-        std::shared_ptr<IndexTracker> tracker;
+-
+-        ITracker& currentTracker = ctx.currentTracker();
+-        if( ITrackerPtr childTracker = currentTracker.findChild( nameAndLocation ) ) {
+-            assert( childTracker );
+-            assert( childTracker->isIndexTracker() );
+-            tracker = std::static_pointer_cast<IndexTracker>( childTracker );
+-        }
+-        else {
+-            tracker = std::make_shared<IndexTracker>( nameAndLocation, ctx, &currentTracker, size );
+-            currentTracker.addChild( tracker );
+-        }
+-
+-        if( !ctx.completedCycle() && !tracker->isComplete() ) {
+-            if( tracker->m_runState != ExecutingChildren && tracker->m_runState != NeedsAnotherRun )
+-                tracker->moveNext();
+-            tracker->open();
+-        }
+-
+-        return *tracker;
++            m_filters.insert( m_filters.end(), filters.begin()+1, filters.end() );
+     }
+ 
+-    int IndexTracker::index() const { return m_index; }
+-
+-    void IndexTracker::moveNext() {
+-        m_index++;
+-        m_children.clear();
++    std::vector<std::string> const& SectionTracker::getFilters() const {
++        return m_filters;
+     }
+ 
+-    void IndexTracker::close() {
+-        TrackerBase::close();
+-        if( m_runState == CompletedSuccessfully && m_index < m_size-1 )
+-            m_runState = Executing;
++    std::string const& SectionTracker::trimmedName() const {
++        return m_trimmed_name;
+     }
+ 
+ } // namespace TestCaseTracking
+@@ -11541,7 +14577,6 @@
+ using TestCaseTracking::ITracker;
+ using TestCaseTracking::TrackerContext;
+ using TestCaseTracking::SectionTracker;
+-using TestCaseTracking::IndexTracker;
+ 
+ } // namespace Catch
+ 
+@@ -11586,47 +14621,81 @@
+ 
+ namespace Catch {
+ 
++    TestSpec::Pattern::Pattern( std::string const& name )
++    : m_name( name )
++    {}
++
+     TestSpec::Pattern::~Pattern() = default;
+-    TestSpec::NamePattern::~NamePattern() = default;
+-    TestSpec::TagPattern::~TagPattern() = default;
+-    TestSpec::ExcludedPattern::~ExcludedPattern() = default;
+ 
+-    TestSpec::NamePattern::NamePattern( std::string const& name )
+-    : m_wildcardPattern( toLower( name ), CaseSensitive::No )
++    std::string const& TestSpec::Pattern::name() const {
++        return m_name;
++    }
++
++    TestSpec::NamePattern::NamePattern( std::string const& name, std::string const& filterString )
++    : Pattern( filterString )
++    , m_wildcardPattern( toLower( name ), CaseSensitive::No )
+     {}
++
+     bool TestSpec::NamePattern::matches( TestCaseInfo const& testCase ) const {
+-        return m_wildcardPattern.matches( toLower( testCase.name ) );
++        return m_wildcardPattern.matches( testCase.name );
+     }
+ 
+-    TestSpec::TagPattern::TagPattern( std::string const& tag ) : m_tag( toLower( tag ) ) {}
++    TestSpec::TagPattern::TagPattern( std::string const& tag, std::string const& filterString )
++    : Pattern( filterString )
++    , m_tag( toLower( tag ) )
++    {}
++
+     bool TestSpec::TagPattern::matches( TestCaseInfo const& testCase ) const {
+         return std::find(begin(testCase.lcaseTags),
+                          end(testCase.lcaseTags),
+                          m_tag) != end(testCase.lcaseTags);
+     }
+ 
+-    TestSpec::ExcludedPattern::ExcludedPattern( PatternPtr const& underlyingPattern ) : m_underlyingPattern( underlyingPattern ) {}
+-    bool TestSpec::ExcludedPattern::matches( TestCaseInfo const& testCase ) const { return !m_underlyingPattern->matches( testCase ); }
++    TestSpec::ExcludedPattern::ExcludedPattern( PatternPtr const& underlyingPattern )
++    : Pattern( underlyingPattern->name() )
++    , m_underlyingPattern( underlyingPattern )
++    {}
++
++    bool TestSpec::ExcludedPattern::matches( TestCaseInfo const& testCase ) const {
++        return !m_underlyingPattern->matches( testCase );
++    }
+ 
+     bool TestSpec::Filter::matches( TestCaseInfo const& testCase ) const {
+-        // All patterns in a filter must match for the filter to be a match
+-        for( auto const& pattern : m_patterns ) {
+-            if( !pattern->matches( testCase ) )
+-                return false;
+-        }
+-        return true;
++        return std::all_of( m_patterns.begin(), m_patterns.end(), [&]( PatternPtr const& p ){ return p->matches( testCase ); } );
++    }
++
++    std::string TestSpec::Filter::name() const {
++        std::string name;
++        for( auto const& p : m_patterns )
++            name += p->name();
++        return name;
+     }
+ 
+     bool TestSpec::hasFilters() const {
+         return !m_filters.empty();
+     }
++
+     bool TestSpec::matches( TestCaseInfo const& testCase ) const {
+-        // A TestSpec matches if any filter matches
+-        for( auto const& filter : m_filters )
+-            if( filter.matches( testCase ) )
+-                return true;
+-        return false;
++        return std::any_of( m_filters.begin(), m_filters.end(), [&]( Filter const& f ){ return f.matches( testCase ); } );
++    }
++
++    TestSpec::Matches TestSpec::matchesByFilter( std::vector<TestCase> const& testCases, IConfig const& config ) const
++    {
++        Matches matches( m_filters.size() );
++        std::transform( m_filters.begin(), m_filters.end(), matches.begin(), [&]( Filter const& filter ){
++            std::vector<TestCase const*> currentMatches;
++            for( auto const& test : testCases )
++                if( isThrowSafe( test, config ) && filter.matches( test ) )
++                    currentMatches.emplace_back( &test );
++            return FilterMatch{ filter.name(), currentMatches };
++        } );
++        return matches;
++    }
++
++    const TestSpec::vectorStrings& TestSpec::getInvalidArgs() const{
++        return  (m_invalidArgs);
+     }
++
+ }
+ // end catch_test_spec.cpp
+ // start catch_test_spec_parser.cpp
+@@ -11638,64 +14707,136 @@
+     TestSpecParser& TestSpecParser::parse( std::string const& arg ) {
+         m_mode = None;
+         m_exclusion = false;
+-        m_start = std::string::npos;
+         m_arg = m_tagAliases->expandAliases( arg );
+         m_escapeChars.clear();
++        m_substring.reserve(m_arg.size());
++        m_patternName.reserve(m_arg.size());
++        m_realPatternPos = 0;
++
+         for( m_pos = 0; m_pos < m_arg.size(); ++m_pos )
+-            visitChar( m_arg[m_pos] );
+-        if( m_mode == Name )
+-            addPattern<TestSpec::NamePattern>();
++          //if visitChar fails
++           if( !visitChar( m_arg[m_pos] ) ){
++               m_testSpec.m_invalidArgs.push_back(arg);
++               break;
++           }
++        endMode();
+         return *this;
+     }
+     TestSpec TestSpecParser::testSpec() {
+         addFilter();
+         return m_testSpec;
+     }
++    bool TestSpecParser::visitChar( char c ) {
++        if( (m_mode != EscapedName) && (c == '\\') ) {
++            escape();
++            addCharToPattern(c);
++            return true;
++        }else if((m_mode != EscapedName) && (c == ',') )  {
++            return separate();
++        }
+ 
+-    void TestSpecParser::visitChar( char c ) {
+-        if( m_mode == None ) {
+-            switch( c ) {
+-            case ' ': return;
+-            case '~': m_exclusion = true; return;
+-            case '[': return startNewMode( Tag, ++m_pos );
+-            case '"': return startNewMode( QuotedName, ++m_pos );
+-            case '\\': return escape();
+-            default: startNewMode( Name, m_pos ); break;
+-            }
+-        }
+-        if( m_mode == Name ) {
+-            if( c == ',' ) {
+-                addPattern<TestSpec::NamePattern>();
+-                addFilter();
+-            }
+-            else if( c == '[' ) {
+-                if( subString() == "exclude:" )
+-                    m_exclusion = true;
+-                else
+-                    addPattern<TestSpec::NamePattern>();
+-                startNewMode( Tag, ++m_pos );
+-            }
+-            else if( c == '\\' )
+-                escape();
++        switch( m_mode ) {
++        case None:
++            if( processNoneChar( c ) )
++                return true;
++            break;
++        case Name:
++            processNameChar( c );
++            break;
++        case EscapedName:
++            endMode();
++            addCharToPattern(c);
++            return true;
++        default:
++        case Tag:
++        case QuotedName:
++            if( processOtherChar( c ) )
++                return true;
++            break;
++        }
++
++        m_substring += c;
++        if( !isControlChar( c ) ) {
++            m_patternName += c;
++            m_realPatternPos++;
++        }
++        return true;
++    }
++    // Two of the processing methods return true to signal the caller to return
++    // without adding the given character to the current pattern strings
++    bool TestSpecParser::processNoneChar( char c ) {
++        switch( c ) {
++        case ' ':
++            return true;
++        case '~':
++            m_exclusion = true;
++            return false;
++        case '[':
++            startNewMode( Tag );
++            return false;
++        case '"':
++            startNewMode( QuotedName );
++            return false;
++        default:
++            startNewMode( Name );
++            return false;
++        }
++    }
++    void TestSpecParser::processNameChar( char c ) {
++        if( c == '[' ) {
++            if( m_substring == "exclude:" )
++                m_exclusion = true;
++            else
++                endMode();
++            startNewMode( Tag );
+         }
+-        else if( m_mode == EscapedName )
+-            m_mode = Name;
+-        else if( m_mode == QuotedName && c == '"' )
+-            addPattern<TestSpec::NamePattern>();
+-        else if( m_mode == Tag && c == ']' )
+-            addPattern<TestSpec::TagPattern>();
+     }
+-    void TestSpecParser::startNewMode( Mode mode, std::size_t start ) {
++    bool TestSpecParser::processOtherChar( char c ) {
++        if( !isControlChar( c ) )
++            return false;
++        m_substring += c;
++        endMode();
++        return true;
++    }
++    void TestSpecParser::startNewMode( Mode mode ) {
+         m_mode = mode;
+-        m_start = start;
++    }
++    void TestSpecParser::endMode() {
++        switch( m_mode ) {
++        case Name:
++        case QuotedName:
++            return addNamePattern();
++        case Tag:
++            return addTagPattern();
++        case EscapedName:
++            revertBackToLastMode();
++            return;
++        case None:
++        default:
++            return startNewMode( None );
++        }
+     }
+     void TestSpecParser::escape() {
+-        if( m_mode == None )
+-            m_start = m_pos;
++        saveLastMode();
+         m_mode = EscapedName;
+-        m_escapeChars.push_back( m_pos );
++        m_escapeChars.push_back(m_realPatternPos);
++    }
++    bool TestSpecParser::isControlChar( char c ) const {
++        switch( m_mode ) {
++            default:
++                return false;
++            case None:
++                return c == '~';
++            case Name:
++                return c == '[';
++            case EscapedName:
++                return true;
++            case QuotedName:
++                return c == '"';
++            case Tag:
++                return c == '[' || c == ']';
++        }
+     }
+-    std::string TestSpecParser::subString() const { return m_arg.substr( m_start, m_pos - m_start ); }
+ 
+     void TestSpecParser::addFilter() {
+         if( !m_currentFilter.m_patterns.empty() ) {
+@@ -11704,6 +14845,86 @@
+         }
+     }
+ 
++    void TestSpecParser::saveLastMode() {
++      lastMode = m_mode;
++    }
++
++    void TestSpecParser::revertBackToLastMode() {
++      m_mode = lastMode;
++    }
++
++    bool TestSpecParser::separate() {
++      if( (m_mode==QuotedName) || (m_mode==Tag) ){
++         //invalid argument, signal failure to previous scope.
++         m_mode = None;
++         m_pos = m_arg.size();
++         m_substring.clear();
++         m_patternName.clear();
++         m_realPatternPos = 0;
++         return false;
++      }
++      endMode();
++      addFilter();
++      return true; //success
++    }
++
++    std::string TestSpecParser::preprocessPattern() {
++        std::string token = m_patternName;
++        for (std::size_t i = 0; i < m_escapeChars.size(); ++i)
++            token = token.substr(0, m_escapeChars[i] - i) + token.substr(m_escapeChars[i] - i + 1);
++        m_escapeChars.clear();
++        if (startsWith(token, "exclude:")) {
++            m_exclusion = true;
++            token = token.substr(8);
++        }
++
++        m_patternName.clear();
++        m_realPatternPos = 0;
++
++        return token;
++    }
++
++    void TestSpecParser::addNamePattern() {
++        auto token = preprocessPattern();
++
++        if (!token.empty()) {
++            TestSpec::PatternPtr pattern = std::make_shared<TestSpec::NamePattern>(token, m_substring);
++            if (m_exclusion)
++                pattern = std::make_shared<TestSpec::ExcludedPattern>(pattern);
++            m_currentFilter.m_patterns.push_back(pattern);
++        }
++        m_substring.clear();
++        m_exclusion = false;
++        m_mode = None;
++    }
++
++    void TestSpecParser::addTagPattern() {
++        auto token = preprocessPattern();
++
++        if (!token.empty()) {
++            // If the tag pattern is the "hide and tag" shorthand (e.g. [.foo])
++            // we have to create a separate hide tag and shorten the real one
++            if (token.size() > 1 && token[0] == '.') {
++                token.erase(token.begin());
++                TestSpec::PatternPtr pattern = std::make_shared<TestSpec::TagPattern>(".", m_substring);
++                if (m_exclusion) {
++                    pattern = std::make_shared<TestSpec::ExcludedPattern>(pattern);
++                }
++                m_currentFilter.m_patterns.push_back(pattern);
++            }
++
++            TestSpec::PatternPtr pattern = std::make_shared<TestSpec::TagPattern>(token, m_substring);
++
++            if (m_exclusion) {
++                pattern = std::make_shared<TestSpec::ExcludedPattern>(pattern);
++            }
++            m_currentFilter.m_patterns.push_back(pattern);
++        }
++        m_substring.clear();
++        m_exclusion = false;
++        m_mode = None;
++    }
++
+     TestSpec parseTestSpec( std::string const& arg ) {
+         return TestSpecParser( ITagAliasRegistry::get() ).parse( arg ).testSpec();
+     }
+@@ -11744,7 +14965,7 @@
+                 // is terrible and we should move on.
+                 // TBD: How to signal that the measured resolution is probably wrong?
+                 if (ticks > startTime + 3 * nanosecondsInSecond) {
+-                    return sum / i;
++                    return sum / ( i + 1u );
+                 }
+             }
+ 
+@@ -11805,13 +15026,11 @@
+             enum Arch { Big, Little };
+ 
+             static Arch which() {
+-                union _{
+-                    int asInt;
+-                    char asChar[sizeof (int)];
+-                } u;
+-
+-                u.asInt = 1;
+-                return ( u.asChar[sizeof(int)-1] == 1 ) ? Big : Little;
++                int one = 1;
++                // If the lowest byte we read is non-zero, we can assume
++                // that little endian format is used.
++                auto value = *reinterpret_cast<char*>(&one);
++                return value ? Little : Big;
+             }
+         };
+     }
+@@ -11935,6 +15154,13 @@
+ }
+ #endif
+ 
++#if defined(CATCH_CONFIG_CPP17_BYTE)
++#include <cstddef>
++std::string StringMaker<std::byte>::convert(std::byte value) {
++    return ::Catch::Detail::stringify(std::to_integer<unsigned long long>(value));
++}
++#endif // defined(CATCH_CONFIG_CPP17_BYTE)
++
+ std::string StringMaker<int>::convert(int value) {
+     return ::Catch::Detail::stringify(static_cast<long long>(value));
+ }
+@@ -11997,11 +15223,16 @@
+     return "nullptr";
+ }
+ 
++int StringMaker<float>::precision = 5;
++
+ std::string StringMaker<float>::convert(float value) {
+-    return fpToString(value, 5) + 'f';
++    return fpToString(value, precision) + 'f';
+ }
++
++int StringMaker<double>::precision = 10;
++
+ std::string StringMaker<double>::convert(double value) {
+-    return fpToString(value, 10);
++    return fpToString(value, precision);
+ }
+ 
+ std::string ratio_string<std::atto>::symbol() { return "a"; }
+@@ -12075,11 +15306,48 @@
+ // end catch_totals.cpp
+ // start catch_uncaught_exceptions.cpp
+ 
++// start catch_config_uncaught_exceptions.hpp
++
++//              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_CONFIG_UNCAUGHT_EXCEPTIONS_HPP
++#define CATCH_CONFIG_UNCAUGHT_EXCEPTIONS_HPP
++
++#if defined(_MSC_VER)
++#  if _MSC_VER >= 1900 // Visual Studio 2015 or newer
++#    define CATCH_INTERNAL_CONFIG_CPP17_UNCAUGHT_EXCEPTIONS
++#  endif
++#endif
++
++#include <exception>
++
++#if defined(__cpp_lib_uncaught_exceptions) \
++    && !defined(CATCH_INTERNAL_CONFIG_CPP17_UNCAUGHT_EXCEPTIONS)
++
++#  define CATCH_INTERNAL_CONFIG_CPP17_UNCAUGHT_EXCEPTIONS
++#endif // __cpp_lib_uncaught_exceptions
++
++#if defined(CATCH_INTERNAL_CONFIG_CPP17_UNCAUGHT_EXCEPTIONS) \
++    && !defined(CATCH_CONFIG_NO_CPP17_UNCAUGHT_EXCEPTIONS) \
++    && !defined(CATCH_CONFIG_CPP17_UNCAUGHT_EXCEPTIONS)
++
++#  define CATCH_CONFIG_CPP17_UNCAUGHT_EXCEPTIONS
++#endif
++
++#endif // CATCH_CONFIG_UNCAUGHT_EXCEPTIONS_HPP
++// end catch_config_uncaught_exceptions.hpp
+ #include <exception>
+ 
+ namespace Catch {
+     bool uncaught_exceptions() {
+-#if defined(CATCH_CONFIG_CPP17_UNCAUGHT_EXCEPTIONS)
++#if defined(CATCH_CONFIG_DISABLE_EXCEPTIONS)
++        return false;
++#elif defined(CATCH_CONFIG_CPP17_UNCAUGHT_EXCEPTIONS)
+         return std::uncaught_exceptions() > 0;
+ #else
+         return std::uncaught_exception();
+@@ -12119,7 +15387,7 @@
+     }
+ 
+     Version const& libraryVersion() {
+-        static Version version( 2, 5, 0, "", 0 );
++        static Version version( 2, 13, 8, "", 0 );
+         return version;
+     }
+ 
+@@ -12127,14 +15395,12 @@
+ // end catch_version.cpp
+ // start catch_wildcard_pattern.cpp
+ 
+-#include <sstream>
+-
+ namespace Catch {
+ 
+     WildcardPattern::WildcardPattern( std::string const& pattern,
+                                       CaseSensitive::Choice caseSensitivity )
+     :   m_caseSensitivity( caseSensitivity ),
+-        m_pattern( adjustCase( pattern ) )
++        m_pattern( normaliseString( pattern ) )
+     {
+         if( startsWith( m_pattern, '*' ) ) {
+             m_pattern = m_pattern.substr( 1 );
+@@ -12149,28 +15415,27 @@
+     bool WildcardPattern::matches( std::string const& str ) const {
+         switch( m_wildcard ) {
+             case NoWildcard:
+-                return m_pattern == adjustCase( str );
++                return m_pattern == normaliseString( str );
+             case WildcardAtStart:
+-                return endsWith( adjustCase( str ), m_pattern );
++                return endsWith( normaliseString( str ), m_pattern );
+             case WildcardAtEnd:
+-                return startsWith( adjustCase( str ), m_pattern );
++                return startsWith( normaliseString( str ), m_pattern );
+             case WildcardAtBothEnds:
+-                return contains( adjustCase( str ), m_pattern );
++                return contains( normaliseString( str ), m_pattern );
+             default:
+                 CATCH_INTERNAL_ERROR( "Unknown enum" );
+         }
+     }
+ 
+-    std::string WildcardPattern::adjustCase( std::string const& str ) const {
+-        return m_caseSensitivity == CaseSensitive::No ? toLower( str ) : str;
++    std::string WildcardPattern::normaliseString( std::string const& str ) const {
++        return trim( m_caseSensitivity == CaseSensitive::No ? toLower( str ) : str );
+     }
+ }
+ // end catch_wildcard_pattern.cpp
+ // start catch_xmlwriter.cpp
+ 
+ #include <iomanip>
+-
+-using uchar = unsigned char;
++#include <type_traits>
+ 
+ namespace Catch {
+ 
+@@ -12203,13 +15468,37 @@
+     }
+ 
+     void hexEscapeChar(std::ostream& os, unsigned char c) {
++        std::ios_base::fmtflags f(os.flags());
+         os << "\\x"
+             << std::uppercase << std::hex << std::setfill('0') << std::setw(2)
+             << static_cast<int>(c);
++        os.flags(f);
++    }
++
++    bool shouldNewline(XmlFormatting fmt) {
++        return !!(static_cast<std::underlying_type<XmlFormatting>::type>(fmt & XmlFormatting::Newline));
++    }
++
++    bool shouldIndent(XmlFormatting fmt) {
++        return !!(static_cast<std::underlying_type<XmlFormatting>::type>(fmt & XmlFormatting::Indent));
+     }
+ 
+ } // anonymous namespace
+ 
++    XmlFormatting operator | (XmlFormatting lhs, XmlFormatting rhs) {
++        return static_cast<XmlFormatting>(
++            static_cast<std::underlying_type<XmlFormatting>::type>(lhs) |
++            static_cast<std::underlying_type<XmlFormatting>::type>(rhs)
++        );
++    }
++
++    XmlFormatting operator & (XmlFormatting lhs, XmlFormatting rhs) {
++        return static_cast<XmlFormatting>(
++            static_cast<std::underlying_type<XmlFormatting>::type>(lhs) &
++            static_cast<std::underlying_type<XmlFormatting>::type>(rhs)
++        );
++    }
++
+     XmlEncode::XmlEncode( std::string const& str, ForWhat forWhat )
+     :   m_str( str ),
+         m_forWhat( forWhat )
+@@ -12220,7 +15509,7 @@
+         // (see: http://www.w3.org/TR/xml/#syntax)
+ 
+         for( std::size_t idx = 0; idx < m_str.size(); ++ idx ) {
+-            uchar c = m_str[idx];
++            unsigned char c = m_str[idx];
+             switch (c) {
+             case '<':   os << "<"; break;
+             case '&':   os << "&"; break;
+@@ -12280,7 +15569,7 @@
+                 bool valid = true;
+                 uint32_t value = headerValue(c);
+                 for (std::size_t n = 1; n < encBytes; ++n) {
+-                    uchar nc = m_str[idx + n];
++                    unsigned char nc = m_str[idx + n];
+                     valid &= ((nc & 0xC0) == 0x80);
+                     value = (value << 6) | (nc & 0x3F);
+                 }
+@@ -12314,13 +15603,17 @@
+         return os;
+     }
+ 
+-    XmlWriter::ScopedElement::ScopedElement( XmlWriter* writer )
+-    :   m_writer( writer )
++    XmlWriter::ScopedElement::ScopedElement( XmlWriter* writer, XmlFormatting fmt )
++    :   m_writer( writer ),
++        m_fmt(fmt)
+     {}
+ 
+     XmlWriter::ScopedElement::ScopedElement( ScopedElement&& other ) noexcept
+-    :   m_writer( other.m_writer ){
++    :   m_writer( other.m_writer ),
++        m_fmt(other.m_fmt)
++    {
+         other.m_writer = nullptr;
++        other.m_fmt = XmlFormatting::None;
+     }
+     XmlWriter::ScopedElement& XmlWriter::ScopedElement::operator=( ScopedElement&& other ) noexcept {
+         if ( m_writer ) {
+@@ -12328,16 +15621,19 @@
+         }
+         m_writer = other.m_writer;
+         other.m_writer = nullptr;
++        m_fmt = other.m_fmt;
++        other.m_fmt = XmlFormatting::None;
+         return *this;
+     }
+ 
+     XmlWriter::ScopedElement::~ScopedElement() {
+-        if( m_writer )
+-            m_writer->endElement();
++        if (m_writer) {
++            m_writer->endElement(m_fmt);
++        }
+     }
+ 
+-    XmlWriter::ScopedElement& XmlWriter::ScopedElement::writeText( std::string const& text, bool indent ) {
+-        m_writer->writeText( text, indent );
++    XmlWriter::ScopedElement& XmlWriter::ScopedElement::writeText( std::string const& text, XmlFormatting fmt ) {
++        m_writer->writeText( text, fmt );
+         return *this;
+     }
+ 
+@@ -12347,37 +15643,47 @@
+     }
+ 
+     XmlWriter::~XmlWriter() {
+-        while( !m_tags.empty() )
++        while (!m_tags.empty()) {
+             endElement();
++        }
++        newlineIfNecessary();
+     }
+ 
+-    XmlWriter& XmlWriter::startElement( std::string const& name ) {
++    XmlWriter& XmlWriter::startElement( std::string const& name, XmlFormatting fmt ) {
+         ensureTagClosed();
+         newlineIfNecessary();
+-        m_os << m_indent << '<' << name;
++        if (shouldIndent(fmt)) {
++            m_os << m_indent;
++            m_indent += "  ";
++        }
++        m_os << '<' << name;
+         m_tags.push_back( name );
+-        m_indent += "  ";
+         m_tagIsOpen = true;
++        applyFormatting(fmt);
+         return *this;
+     }
+ 
+-    XmlWriter::ScopedElement XmlWriter::scopedElement( std::string const& name ) {
+-        ScopedElement scoped( this );
+-        startElement( name );
++    XmlWriter::ScopedElement XmlWriter::scopedElement( std::string const& name, XmlFormatting fmt ) {
++        ScopedElement scoped( this, fmt );
++        startElement( name, fmt );
+         return scoped;
+     }
+ 
+-    XmlWriter& XmlWriter::endElement() {
+-        newlineIfNecessary();
+-        m_indent = m_indent.substr( 0, m_indent.size()-2 );
++    XmlWriter& XmlWriter::endElement(XmlFormatting fmt) {
++        m_indent = m_indent.substr(0, m_indent.size() - 2);
++
+         if( m_tagIsOpen ) {
+             m_os << "/>";
+             m_tagIsOpen = false;
++        } else {
++            newlineIfNecessary();
++            if (shouldIndent(fmt)) {
++                m_os << m_indent;
++            }
++            m_os << "</" << m_tags.back() << ">";
+         }
+-        else {
+-            m_os << m_indent << "</" << m_tags.back() << ">";
+-        }
+-        m_os << std::endl;
++        m_os << std::flush;
++        applyFormatting(fmt);
+         m_tags.pop_back();
+         return *this;
+     }
+@@ -12393,22 +15699,26 @@
+         return *this;
+     }
+ 
+-    XmlWriter& XmlWriter::writeText( std::string const& text, bool indent ) {
++    XmlWriter& XmlWriter::writeText( std::string const& text, XmlFormatting fmt) {
+         if( !text.empty() ){
+             bool tagWasOpen = m_tagIsOpen;
+             ensureTagClosed();
+-            if( tagWasOpen && indent )
++            if (tagWasOpen && shouldIndent(fmt)) {
+                 m_os << m_indent;
++            }
+             m_os << XmlEncode( text );
+-            m_needsNewline = true;
++            applyFormatting(fmt);
+         }
+         return *this;
+     }
+ 
+-    XmlWriter& XmlWriter::writeComment( std::string const& text ) {
++    XmlWriter& XmlWriter::writeComment( std::string const& text, XmlFormatting fmt) {
+         ensureTagClosed();
+-        m_os << m_indent << "<!--" << text << "-->";
+-        m_needsNewline = true;
++        if (shouldIndent(fmt)) {
++            m_os << m_indent;
++        }
++        m_os << "<!--" << text << "-->";
++        applyFormatting(fmt);
+         return *this;
+     }
+ 
+@@ -12424,11 +15734,16 @@
+ 
+     void XmlWriter::ensureTagClosed() {
+         if( m_tagIsOpen ) {
+-            m_os << ">" << std::endl;
++            m_os << '>' << std::flush;
++            newlineIfNecessary();
+             m_tagIsOpen = false;
+         }
+     }
+ 
++    void XmlWriter::applyFormatting(XmlFormatting fmt) {
++        m_needsNewline = shouldNewline(fmt);
++    }
++
+     void XmlWriter::writeDeclaration() {
+         m_os << "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n";
+     }
+@@ -12469,11 +15784,37 @@
+ #ifdef _MSC_VER
+         sprintf_s(buffer, "%.3f", duration);
+ #else
+-        sprintf(buffer, "%.3f", duration);
++        std::sprintf(buffer, "%.3f", duration);
+ #endif
+         return std::string(buffer);
+     }
+ 
++    bool shouldShowDuration( IConfig const& config, double duration ) {
++        if ( config.showDurations() == ShowDurations::Always ) {
++            return true;
++        }
++        if ( config.showDurations() == ShowDurations::Never ) {
++            return false;
++        }
++        const double min = config.minDuration();
++        return min >= 0 && duration >= min;
++    }
++
++    std::string serializeFilters( std::vector<std::string> const& container ) {
++        ReusableStringStream oss;
++        bool first = true;
++        for (auto&& filter : container)
++        {
++            if (!first)
++                oss << ' ';
++            else
++                first = false;
++
++            oss << filter;
++        }
++        return oss.str();
++    }
++
+     TestEventListenerBase::TestEventListenerBase(ReporterConfig const & _config)
+         :StreamingReporterBase(_config) {}
+ 
+@@ -12689,24 +16030,25 @@
+         if (itMessage == messages.end())
+             return;
+ 
+-        // using messages.end() directly yields (or auto) compilation error:
+-        std::vector<MessageInfo>::const_iterator itEnd = messages.end();
+-        const std::size_t N = static_cast<std::size_t>(std::distance(itMessage, itEnd));
++        const auto itEnd = messages.cend();
++        const auto N = static_cast<std::size_t>(std::distance(itMessage, itEnd));
+ 
+         {
+             Colour colourGuard(colour);
+             stream << " with " << pluralise(N, "message") << ':';
+         }
+ 
+-        for (; itMessage != itEnd; ) {
++        while (itMessage != itEnd) {
+             // If this assertion is a warning ignore any INFO messages
+             if (printInfoMessages || itMessage->type != ResultWas::Info) {
+-                stream << " '" << itMessage->message << '\'';
+-                if (++itMessage != itEnd) {
++                printMessage();
++                if (itMessage != itEnd) {
+                     Colour colourGuard(dimColour());
+                     stream << " and";
+                 }
++                continue;
+             }
++            ++itMessage;
+         }
+     }
+ 
+@@ -12724,10 +16066,6 @@
+             return "Reports test results on a single line, suitable for IDEs";
+         }
+ 
+-        ReporterPreferences CompactReporter::getPreferences() const {
+-            return m_reporterPrefs;
+-        }
+-
+         void CompactReporter::noMatchingTestCases( std::string const& spec ) {
+             stream << "No test cases matched '" << spec << '\'' << std::endl;
+         }
+@@ -12754,8 +16092,9 @@
+         }
+ 
+         void CompactReporter::sectionEnded(SectionStats const& _sectionStats) {
+-            if (m_config->showDurations() == ShowDurations::Always) {
+-                stream << getFormattedDuration(_sectionStats.durationInSeconds) << " s: " << _sectionStats.sectionInfo.name << std::endl;
++            double dur = _sectionStats.durationInSeconds;
++            if ( shouldShowDuration( *m_config, dur ) ) {
++                stream << getFormattedDuration( dur ) << " s: " << _sectionStats.sectionInfo.name << std::endl;
+             }
+         }
+ 
+@@ -12779,8 +16118,13 @@
+ #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
++ // Note that 4062 (not all labels are handled and default is missing) is enabled
++#endif
++
++#if defined(__clang__)
++#  pragma clang diagnostic push
++// For simplicity, benchmarking-only helpers are always enabled
++#  pragma clang diagnostic ignored "-Wunused-function"
+ #endif
+ 
+ namespace Catch {
+@@ -12962,11 +16306,11 @@
+     static const uint64_t s_nanosecondsInASecond = 1000 * s_nanosecondsInAMillisecond;
+     static const uint64_t s_nanosecondsInAMinute = 60 * s_nanosecondsInASecond;
+ 
+-    uint64_t m_inNanoseconds;
++    double m_inNanoseconds;
+     Unit m_units;
+ 
+ public:
+-    explicit Duration(uint64_t inNanoseconds, Unit units = Unit::Auto)
++    explicit Duration(double inNanoseconds, Unit units = Unit::Auto)
+         : m_inNanoseconds(inNanoseconds),
+         m_units(units) {
+         if (m_units == Unit::Auto) {
+@@ -12995,7 +16339,7 @@
+         case Unit::Minutes:
+             return m_inNanoseconds / static_cast<double>(s_nanosecondsInAMinute);
+         default:
+-            return static_cast<double>(m_inNanoseconds);
++            return m_inNanoseconds;
+         }
+     }
+     auto unitsAsString() const -> std::string {
+@@ -13003,7 +16347,7 @@
+         case Unit::Nanoseconds:
+             return "ns";
+         case Unit::Microseconds:
+-            return "µs";
++            return "us";
+         case Unit::Milliseconds:
+             return "ms";
+         case Unit::Seconds:
+@@ -13016,7 +16360,7 @@
+ 
+     }
+     friend auto operator << (std::ostream& os, Duration const& duration) -> std::ostream& {
+-        return os << duration.value() << " " << duration.unitsAsString();
++        return os << duration.value() << ' ' << duration.unitsAsString();
+     }
+ };
+ } // end anon namespace
+@@ -13041,10 +16385,16 @@
+         if (!m_isOpen) {
+             m_isOpen = true;
+             *this << RowBreak();
+-            for (auto const& info : m_columnInfos)
+-                *this << info.name << ColumnBreak();
+-            *this << RowBreak();
+-            m_os << Catch::getLineOfChars<'-'>() << "\n";
++
++			Columns headerCols;
++			Spacer spacer(2);
++			for (auto const& info : m_columnInfos) {
++				headerCols += Column(info.name).width(static_cast<std::size_t>(info.width - 2));
++				headerCols += spacer;
++			}
++			m_os << headerCols << '\n';
++
++            m_os << Catch::getLineOfChars<'-'>() << '\n';
+         }
+     }
+     void close() {
+@@ -13063,30 +16413,29 @@
+ 
+     friend TablePrinter& operator << (TablePrinter& tp, ColumnBreak) {
+         auto colStr = tp.m_oss.str();
+-        // This takes account of utf8 encodings
+-        auto strSize = Catch::StringRef(colStr).numberOfCharacters();
++        const auto strSize = colStr.size();
+         tp.m_oss.str("");
+         tp.open();
+         if (tp.m_currentColumn == static_cast<int>(tp.m_columnInfos.size() - 1)) {
+             tp.m_currentColumn = -1;
+-            tp.m_os << "\n";
++            tp.m_os << '\n';
+         }
+         tp.m_currentColumn++;
+ 
+         auto colInfo = tp.m_columnInfos[tp.m_currentColumn];
+-        auto padding = (strSize + 2 < static_cast<std::size_t>(colInfo.width))
+-            ? std::string(colInfo.width - (strSize + 2), ' ')
++        auto padding = (strSize + 1 < static_cast<std::size_t>(colInfo.width))
++            ? std::string(colInfo.width - (strSize + 1), ' ')
+             : std::string();
+         if (colInfo.justification == ColumnInfo::Left)
+-            tp.m_os << colStr << padding << " ";
++            tp.m_os << colStr << padding << ' ';
+         else
+-            tp.m_os << padding << colStr << " ";
++            tp.m_os << padding << colStr << ' ';
+         return tp;
+     }
+ 
+     friend TablePrinter& operator << (TablePrinter& tp, RowBreak) {
+         if (tp.m_currentColumn > 0) {
+-            tp.m_os << "\n";
++            tp.m_os << '\n';
+             tp.m_currentColumn = -1;
+         }
+         return tp;
+@@ -13096,12 +16445,26 @@
+ ConsoleReporter::ConsoleReporter(ReporterConfig const& config)
+     : StreamingReporterBase(config),
+     m_tablePrinter(new TablePrinter(config.stream(),
+-    {
+-        { "benchmark name", CATCH_CONFIG_CONSOLE_WIDTH - 32, ColumnInfo::Left },
+-        { "iters", 8, ColumnInfo::Right },
+-        { "elapsed ns", 14, ColumnInfo::Right },
+-        { "average", 14, ColumnInfo::Right }
+-    })) {}
++        [&config]() -> std::vector<ColumnInfo> {
++        if (config.fullConfig()->benchmarkNoAnalysis())
++        {
++            return{
++                { "benchmark name", CATCH_CONFIG_CONSOLE_WIDTH - 43, ColumnInfo::Left },
++                { "     samples", 14, ColumnInfo::Right },
++                { "  iterations", 14, ColumnInfo::Right },
++                { "        mean", 14, ColumnInfo::Right }
++            };
++        }
++        else
++        {
++            return{
++                { "benchmark name", CATCH_CONFIG_CONSOLE_WIDTH - 43, ColumnInfo::Left },
++                { "samples      mean       std dev", 14, ColumnInfo::Right },
++                { "iterations   low mean   low std dev", 14, ColumnInfo::Right },
++                { "estimated    high mean  high std dev", 14, ColumnInfo::Right }
++            };
++        }
++    }())) {}
+ ConsoleReporter::~ConsoleReporter() = default;
+ 
+ std::string ConsoleReporter::getDescription() {
+@@ -13112,6 +16475,10 @@
+     stream << "No test cases matched '" << spec << '\'' << std::endl;
+ }
+ 
++void ConsoleReporter::reportInvalidArguments(std::string const&arg){
++    stream << "Invalid Filter: " << arg << std::endl;
++}
++
+ void ConsoleReporter::assertionStarting(AssertionInfo const&) {}
+ 
+ bool ConsoleReporter::assertionEnded(AssertionStats const& _assertionStats) {
+@@ -13132,6 +16499,7 @@
+ }
+ 
+ void ConsoleReporter::sectionStarting(SectionInfo const& _sectionInfo) {
++    m_tablePrinter->close();
+     m_headerPrinted = false;
+     StreamingReporterBase::sectionStarting(_sectionInfo);
+ }
+@@ -13146,8 +16514,9 @@
+             stream << "\nNo assertions in test case";
+         stream << " '" << _sectionStats.sectionInfo.name << "'\n" << std::endl;
+     }
+-    if (m_config->showDurations() == ShowDurations::Always) {
+-        stream << getFormattedDuration(_sectionStats.durationInSeconds) << " s: " << _sectionStats.sectionInfo.name << std::endl;
++    double dur = _sectionStats.durationInSeconds;
++    if (shouldShowDuration(*m_config, dur)) {
++        stream << getFormattedDuration(dur) << " s: " << _sectionStats.sectionInfo.name << std::endl;
+     }
+     if (m_headerPrinted) {
+         m_headerPrinted = false;
+@@ -13155,28 +16524,53 @@
+     StreamingReporterBase::sectionEnded(_sectionStats);
+ }
+ 
+-void ConsoleReporter::benchmarkStarting(BenchmarkInfo const& info) {
+-    lazyPrintWithoutClosingBenchmarkTable();
+-
+-    auto nameCol = Column( info.name ).width( static_cast<std::size_t>( m_tablePrinter->columnInfos()[0].width - 2 ) );
++#if defined(CATCH_CONFIG_ENABLE_BENCHMARKING)
++void ConsoleReporter::benchmarkPreparing(std::string const& name) {
++	lazyPrintWithoutClosingBenchmarkTable();
++
++	auto nameCol = Column(name).width(static_cast<std::size_t>(m_tablePrinter->columnInfos()[0].width - 2));
++
++	bool firstLine = true;
++	for (auto line : nameCol) {
++		if (!firstLine)
++			(*m_tablePrinter) << ColumnBreak() << ColumnBreak() << ColumnBreak();
++		else
++			firstLine = false;
+ 
+-    bool firstLine = true;
+-    for (auto line : nameCol) {
+-        if (!firstLine)
+-            (*m_tablePrinter) << ColumnBreak() << ColumnBreak() << ColumnBreak();
+-        else
+-            firstLine = false;
++		(*m_tablePrinter) << line << ColumnBreak();
++	}
++}
+ 
+-        (*m_tablePrinter) << line << ColumnBreak();
++void ConsoleReporter::benchmarkStarting(BenchmarkInfo const& info) {
++    (*m_tablePrinter) << info.samples << ColumnBreak()
++        << info.iterations << ColumnBreak();
++    if (!m_config->benchmarkNoAnalysis())
++        (*m_tablePrinter) << Duration(info.estimatedDuration) << ColumnBreak();
++}
++void ConsoleReporter::benchmarkEnded(BenchmarkStats<> const& stats) {
++    if (m_config->benchmarkNoAnalysis())
++    {
++        (*m_tablePrinter) << Duration(stats.mean.point.count()) << ColumnBreak();
++    }
++    else
++    {
++        (*m_tablePrinter) << ColumnBreak()
++            << Duration(stats.mean.point.count()) << ColumnBreak()
++            << Duration(stats.mean.lower_bound.count()) << ColumnBreak()
++            << Duration(stats.mean.upper_bound.count()) << ColumnBreak() << ColumnBreak()
++            << Duration(stats.standardDeviation.point.count()) << ColumnBreak()
++            << Duration(stats.standardDeviation.lower_bound.count()) << ColumnBreak()
++            << Duration(stats.standardDeviation.upper_bound.count()) << ColumnBreak() << ColumnBreak() << ColumnBreak() << ColumnBreak() << ColumnBreak();
+     }
+ }
+-void ConsoleReporter::benchmarkEnded(BenchmarkStats const& stats) {
+-    Duration average(stats.elapsedTimeInNanoseconds / stats.iterations);
++
++void ConsoleReporter::benchmarkFailed(std::string const& error) {
++	Colour colour(Colour::Red);
+     (*m_tablePrinter)
+-        << stats.iterations << ColumnBreak()
+-        << stats.elapsedTimeInNanoseconds << ColumnBreak()
+-        << average << ColumnBreak();
++        << "Benchmark failed (" << error << ')'
++        << ColumnBreak() << RowBreak();
+ }
++#endif // CATCH_CONFIG_ENABLE_BENCHMARKING
+ 
+ void ConsoleReporter::testCaseEnded(TestCaseStats const& _testCaseStats) {
+     m_tablePrinter->close();
+@@ -13198,6 +16592,10 @@
+     stream << std::endl;
+     StreamingReporterBase::testRunEnded(_testRunStats);
+ }
++void ConsoleReporter::testRunStarting(TestRunInfo const& _testInfo) {
++    StreamingReporterBase::testRunStarting(_testInfo);
++    printTestFilters();
++}
+ 
+ void ConsoleReporter::lazyPrint() {
+ 
+@@ -13251,11 +16649,9 @@
+ 
+     SourceLineInfo lineInfo = m_sectionStack.back().lineInfo;
+ 
+-    if (!lineInfo.empty()) {
+-        stream << getLineOfChars<'-'>() << '\n';
+-        Colour colourGuard(Colour::FileName);
+-        stream << lineInfo << '\n';
+-    }
++    stream << getLineOfChars<'-'>() << '\n';
++    Colour colourGuard(Colour::FileName);
++    stream << lineInfo << '\n';
+     stream << getLineOfChars<'.'>() << '\n' << std::endl;
+ }
+ 
+@@ -13379,6 +16775,13 @@
+     stream << getLineOfChars<'-'>() << '\n';
+ }
+ 
++void ConsoleReporter::printTestFilters() {
++    if (m_config->testSpec().hasFilters()) {
++        Colour guard(Colour::BrightYellow);
++        stream << "Filters: " << serializeFilters(m_config->getTestsOrTags()) << '\n';
++    }
++}
++
+ CATCH_REGISTER_REPORTER("console", ConsoleReporter)
+ 
+ } // end namespace Catch
+@@ -13386,6 +16789,10 @@
+ #if defined(_MSC_VER)
+ #pragma warning(pop)
+ #endif
++
++#if defined(__clang__)
++#  pragma clang diagnostic pop
++#endif
+ // end catch_reporter_console.cpp
+ // start catch_reporter_junit.cpp
+ 
+@@ -13393,6 +16800,7 @@
+ #include <sstream>
+ #include <ctime>
+ #include <algorithm>
++#include <iomanip>
+ 
+ namespace Catch {
+ 
+@@ -13420,7 +16828,7 @@
+ #else
+             std::strftime(timeStamp, timeStampSize, fmt, timeInfo);
+ #endif
+-            return std::string(timeStamp);
++            return std::string(timeStamp, timeStampSize-1);
+         }
+ 
+         std::string fileNameTag(const std::vector<std::string> &tags) {
+@@ -13431,6 +16839,17 @@
+                 return it->substr(1);
+             return std::string();
+         }
++
++        // Formats the duration in seconds to 3 decimal places.
++        // This is done because some genius defined Maven Surefire schema
++        // in a way that only accepts 3 decimal places, and tools like
++        // Jenkins use that schema for validation JUnit reporter output.
++        std::string formatDuration( double seconds ) {
++            ReusableStringStream rss;
++            rss << std::fixed << std::setprecision( 3 ) << seconds;
++            return rss.str();
++        }
++
+     } // anonymous namespace
+ 
+     JunitReporter::JunitReporter( ReporterConfig const& _config )
+@@ -13490,6 +16909,7 @@
+ 
+     void JunitReporter::writeGroup( TestGroupNode const& groupNode, double suiteTime ) {
+         XmlWriter::ScopedElement e = xml.scopedElement( "testsuite" );
++
+         TestGroupStats const& stats = groupNode.value;
+         xml.writeAttribute( "name", stats.groupInfo.name );
+         xml.writeAttribute( "errors", unexpectedExceptions );
+@@ -13499,15 +16919,30 @@
+         if( m_config->showDurations() == ShowDurations::Never )
+             xml.writeAttribute( "time", "" );
+         else
+-            xml.writeAttribute( "time", suiteTime );
++            xml.writeAttribute( "time", formatDuration( suiteTime ) );
+         xml.writeAttribute( "timestamp", 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()));
++            }
++            if (m_config->rngSeed() != 0) {
++                xml.scopedElement("property")
++                    .writeAttribute("name", "random-seed")
++                    .writeAttribute("value", m_config->rngSeed());
++            }
++        }
++
+         // Write test cases
+         for( auto const& child : groupNode.children )
+             writeTestCase( *child );
+ 
+-        xml.scopedElement( "system-out" ).writeText( trim( stdOutForSuite ), false );
+-        xml.scopedElement( "system-err" ).writeText( trim( stdErrForSuite ), false );
++        xml.scopedElement( "system-out" ).writeText( trim( stdOutForSuite ), XmlFormatting::Newline );
++        xml.scopedElement( "system-err" ).writeText( trim( stdErrForSuite ), XmlFormatting::Newline );
+     }
+ 
+     void JunitReporter::writeTestCase( TestCaseNode const& testCaseNode ) {
+@@ -13529,12 +16964,13 @@
+         if ( !m_config->name().empty() )
+             className = m_config->name() + "." + className;
+ 
+-        writeSection( className, "", rootSection );
++        writeSection( className, "", rootSection, stats.testInfo.okToFail() );
+     }
+ 
+-    void JunitReporter::writeSection(  std::string const& className,
+-                        std::string const& rootName,
+-                        SectionNode const& sectionNode ) {
++    void JunitReporter::writeSection( std::string const& className,
++                                      std::string const& rootName,
++                                      SectionNode const& sectionNode,
++                                      bool testOkToFail) {
+         std::string name = trim( sectionNode.stats.sectionInfo.name );
+         if( !rootName.empty() )
+             name = rootName + '/' + name;
+@@ -13551,20 +16987,30 @@
+                 xml.writeAttribute( "classname", className );
+                 xml.writeAttribute( "name", name );
+             }
+-            xml.writeAttribute( "time", ::Catch::Detail::stringify( sectionNode.stats.durationInSeconds ) );
++            xml.writeAttribute( "time", formatDuration( 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" );
++
++            if (sectionNode.stats.assertions.failedButOk) {
++                xml.scopedElement("skipped")
++                    .writeAttribute("message", "TEST_CASE tagged with !mayfail");
++            }
+ 
+             writeAssertions( sectionNode );
+ 
+             if( !sectionNode.stdOut.empty() )
+-                xml.scopedElement( "system-out" ).writeText( trim( sectionNode.stdOut ), false );
++                xml.scopedElement( "system-out" ).writeText( trim( sectionNode.stdOut ), XmlFormatting::Newline );
+             if( !sectionNode.stdErr.empty() )
+-                xml.scopedElement( "system-err" ).writeText( trim( sectionNode.stdErr ), false );
++                xml.scopedElement( "system-err" ).writeText( trim( sectionNode.stdErr ), XmlFormatting::Newline );
+         }
+         for( auto const& childNode : sectionNode.childSections )
+             if( className.empty() )
+-                writeSection( name, "", *childNode );
++                writeSection( name, "", *childNode, testOkToFail );
+             else
+-                writeSection( className, name, *childNode );
++                writeSection( className, name, *childNode, testOkToFail );
+     }
+ 
+     void JunitReporter::writeAssertions( SectionNode const& sectionNode ) {
+@@ -13582,11 +17028,7 @@
+                     elementName = "error";
+                     break;
+                 case ResultWas::ExplicitFailure:
+-                    elementName = "failure";
+-                    break;
+                 case ResultWas::ExpressionFailed:
+-                    elementName = "failure";
+-                    break;
+                 case ResultWas::DidntThrowException:
+                     elementName = "failure";
+                     break;
+@@ -13604,10 +17046,25 @@
+ 
+             XmlWriter::ScopedElement e = xml.scopedElement( elementName );
+ 
+-            xml.writeAttribute( "message", result.getExpandedExpression() );
++            xml.writeAttribute( "message", result.getExpression() );
+             xml.writeAttribute( "type", result.getTestMacroName() );
+ 
+             ReusableStringStream rss;
++            if (stats.totals.assertions.total() > 0) {
++                rss << "FAILED" << ":\n";
++                if (result.hasExpression()) {
++                    rss << "  ";
++                    rss << result.getExpressionInMacro();
++                    rss << '\n';
++                }
++                if (result.hasExpandedExpression()) {
++                    rss << "with expansion:\n";
++                    rss << Column(result.getExpandedExpression()).indent(2) << '\n';
++                }
++            } else {
++                rss << '\n';
++            }
++
+             if( !result.getMessage().empty() )
+                 rss << result.getMessage() << '\n';
+             for( auto const& msg : stats.infoMessages )
+@@ -13615,7 +17072,7 @@
+                     rss << msg.message << '\n';
+ 
+             rss << "at " << result.getSourceInfo();
+-            xml.writeText( rss.str(), false );
++            xml.writeText( rss.str(), XmlFormatting::Newline );
+         }
+     }
+ 
+@@ -13659,19 +17116,41 @@
+         m_reporter->noMatchingTestCases( spec );
+     }
+ 
++    void ListeningReporter::reportInvalidArguments(std::string const&arg){
++        for ( auto const& listener : m_listeners ) {
++            listener->reportInvalidArguments( arg );
++        }
++        m_reporter->reportInvalidArguments( arg );
++    }
++
++#if defined(CATCH_CONFIG_ENABLE_BENCHMARKING)
++    void ListeningReporter::benchmarkPreparing( std::string const& name ) {
++		for (auto const& listener : m_listeners) {
++			listener->benchmarkPreparing(name);
++		}
++		m_reporter->benchmarkPreparing(name);
++	}
+     void ListeningReporter::benchmarkStarting( BenchmarkInfo const& benchmarkInfo ) {
+         for ( auto const& listener : m_listeners ) {
+             listener->benchmarkStarting( benchmarkInfo );
+         }
+         m_reporter->benchmarkStarting( benchmarkInfo );
+     }
+-    void ListeningReporter::benchmarkEnded( BenchmarkStats const& benchmarkStats ) {
++    void ListeningReporter::benchmarkEnded( BenchmarkStats<> const& benchmarkStats ) {
+         for ( auto const& listener : m_listeners ) {
+             listener->benchmarkEnded( benchmarkStats );
+         }
+         m_reporter->benchmarkEnded( benchmarkStats );
+     }
+ 
++	void ListeningReporter::benchmarkFailed( std::string const& error ) {
++		for (auto const& listener : m_listeners) {
++			listener->benchmarkFailed(error);
++		}
++		m_reporter->benchmarkFailed(error);
++	}
++#endif // CATCH_CONFIG_ENABLE_BENCHMARKING
++
+     void ListeningReporter::testRunStarting( TestRunInfo const& testRunInfo ) {
+         for ( auto const& listener : m_listeners ) {
+             listener->testRunStarting( testRunInfo );
+@@ -13802,6 +17281,8 @@
+         m_xml.startElement( "Catch" );
+         if( !m_config->name().empty() )
+             m_xml.writeAttribute( "name", m_config->name() );
++        if (m_config->testSpec().hasFilters())
++            m_xml.writeAttribute( "filters", serializeFilters( m_config->getTestsOrTags() ) );
+         if( m_config->rngSeed() != 0 )
+             m_xml.scopedElement( "Randomness" )
+                 .writeAttribute( "seed", m_config->rngSeed() );
+@@ -13937,9 +17418,9 @@
+             e.writeAttribute( "durationInSeconds", m_testCaseTimer.getElapsedSeconds() );
+ 
+         if( !testCaseStats.stdOut.empty() )
+-            m_xml.scopedElement( "StdOut" ).writeText( trim( testCaseStats.stdOut ), false );
++            m_xml.scopedElement( "StdOut" ).writeText( trim( testCaseStats.stdOut ), XmlFormatting::Newline );
+         if( !testCaseStats.stdErr.empty() )
+-            m_xml.scopedElement( "StdErr" ).writeText( trim( testCaseStats.stdErr ), false );
++            m_xml.scopedElement( "StdErr" ).writeText( trim( testCaseStats.stdErr ), XmlFormatting::Newline );
+ 
+         m_xml.endElement();
+     }
+@@ -13951,6 +17432,10 @@
+             .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();
+     }
+ 
+@@ -13960,9 +17445,58 @@
+             .writeAttribute( "successes", testRunStats.totals.assertions.passed )
+             .writeAttribute( "failures", testRunStats.totals.assertions.failed )
+             .writeAttribute( "expectedFailures", 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 );
++        m_xml.endElement();
++    }
++
++#if defined(CATCH_CONFIG_ENABLE_BENCHMARKING)
++    void XmlReporter::benchmarkPreparing(std::string const& name) {
++        m_xml.startElement("BenchmarkResults")
++            .writeAttribute("name", 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");
++    }
++
++    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);
++        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);
++        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);
++        m_xml.endElement();
+         m_xml.endElement();
+     }
+ 
++    void XmlReporter::benchmarkFailed(std::string const &error) {
++        m_xml.scopedElement("failed").
++            writeAttribute("message", error);
++        m_xml.endElement();
++    }
++#endif // CATCH_CONFIG_ENABLE_BENCHMARKING
++
+     CATCH_REGISTER_REPORTER( "xml", XmlReporter )
+ 
+ } // end namespace Catch
+@@ -13988,7 +17522,7 @@
+ 
+ #ifndef __OBJC__
+ 
+-#if defined(CATCH_CONFIG_WCHAR) && defined(WIN32) && defined(_UNICODE) && !defined(DO_NOT_USE_WMAIN)
++#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 * []) {
+ #else
+@@ -14036,7 +17570,7 @@
+ #define CATCH_REQUIRE( ... ) INTERNAL_CATCH_TEST( "CATCH_REQUIRE", Catch::ResultDisposition::Normal, __VA_ARGS__ )
+ #define CATCH_REQUIRE_FALSE( ... ) INTERNAL_CATCH_TEST( "CATCH_REQUIRE_FALSE", Catch::ResultDisposition::Normal | Catch::ResultDisposition::FalseTest, __VA_ARGS__ )
+ 
+-#define CATCH_REQUIRE_THROWS( ... ) INTERNAL_CATCH_THROWS( "CATCH_REQUIRE_THROWS", Catch::ResultDisposition::Normal, "", __VA_ARGS__ )
++#define CATCH_REQUIRE_THROWS( ... ) INTERNAL_CATCH_THROWS( "CATCH_REQUIRE_THROWS", Catch::ResultDisposition::Normal, __VA_ARGS__ )
+ #define CATCH_REQUIRE_THROWS_AS( expr, exceptionType ) INTERNAL_CATCH_THROWS_AS( "CATCH_REQUIRE_THROWS_AS", exceptionType, Catch::ResultDisposition::Normal, expr )
+ #define CATCH_REQUIRE_THROWS_WITH( expr, matcher ) INTERNAL_CATCH_THROWS_STR_MATCHES( "CATCH_REQUIRE_THROWS_WITH", Catch::ResultDisposition::Normal, matcher, expr )
+ #if !defined(CATCH_CONFIG_DISABLE_MATCHERS)
+@@ -14050,7 +17584,7 @@
+ #define CATCH_CHECKED_ELSE( ... ) INTERNAL_CATCH_ELSE( "CATCH_CHECKED_ELSE", Catch::ResultDisposition::ContinueOnFailure, __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__ )
++#define CATCH_CHECK_THROWS( ... )  INTERNAL_CATCH_THROWS( "CATCH_CHECK_THROWS", Catch::ResultDisposition::ContinueOnFailure, __VA_ARGS__ )
+ #define CATCH_CHECK_THROWS_AS( expr, exceptionType ) INTERNAL_CATCH_THROWS_AS( "CATCH_CHECK_THROWS_AS", exceptionType, Catch::ResultDisposition::ContinueOnFailure, expr )
+ #define CATCH_CHECK_THROWS_WITH( expr, matcher ) INTERNAL_CATCH_THROWS_STR_MATCHES( "CATCH_CHECK_THROWS_WITH", Catch::ResultDisposition::ContinueOnFailure, matcher, expr )
+ #if !defined(CATCH_CONFIG_DISABLE_MATCHERS)
+@@ -14065,6 +17599,7 @@
+ #endif // CATCH_CONFIG_DISABLE_MATCHERS
+ 
+ #define CATCH_INFO( msg ) INTERNAL_CATCH_INFO( "CATCH_INFO", msg )
++#define CATCH_UNSCOPED_INFO( msg ) INTERNAL_CATCH_UNSCOPED_INFO( "CATCH_UNSCOPED_INFO", msg )
+ #define CATCH_WARN( msg ) INTERNAL_CATCH_MSG( "CATCH_WARN", Catch::ResultWas::Warning, Catch::ResultDisposition::ContinueOnFailure, msg )
+ #define CATCH_CAPTURE( ... ) INTERNAL_CATCH_CAPTURE( INTERNAL_CATCH_UNIQUE_NAME(capturer), "CATCH_CAPTURE",__VA_ARGS__ )
+ 
+@@ -14082,10 +17617,22 @@
+ 
+ #ifndef CATCH_CONFIG_TRADITIONAL_MSVC_PREPROCESSOR
+ #define CATCH_TEMPLATE_TEST_CASE( ... ) INTERNAL_CATCH_TEMPLATE_TEST_CASE( __VA_ARGS__ )
++#define CATCH_TEMPLATE_TEST_CASE_SIG( ... ) INTERNAL_CATCH_TEMPLATE_TEST_CASE_SIG( __VA_ARGS__ )
+ #define CATCH_TEMPLATE_TEST_CASE_METHOD( className, ... ) INTERNAL_CATCH_TEMPLATE_TEST_CASE_METHOD( className, __VA_ARGS__ )
++#define CATCH_TEMPLATE_TEST_CASE_METHOD_SIG( className, ... ) INTERNAL_CATCH_TEMPLATE_TEST_CASE_METHOD_SIG( className, __VA_ARGS__ )
++#define CATCH_TEMPLATE_PRODUCT_TEST_CASE( ... ) INTERNAL_CATCH_TEMPLATE_PRODUCT_TEST_CASE( __VA_ARGS__ )
++#define CATCH_TEMPLATE_PRODUCT_TEST_CASE_SIG( ... ) INTERNAL_CATCH_TEMPLATE_PRODUCT_TEST_CASE_SIG( __VA_ARGS__ )
++#define CATCH_TEMPLATE_PRODUCT_TEST_CASE_METHOD( className, ... ) INTERNAL_CATCH_TEMPLATE_PRODUCT_TEST_CASE_METHOD( className, __VA_ARGS__ )
++#define CATCH_TEMPLATE_PRODUCT_TEST_CASE_METHOD_SIG( className, ... ) INTERNAL_CATCH_TEMPLATE_PRODUCT_TEST_CASE_METHOD_SIG( className, __VA_ARGS__ )
+ #else
+ #define CATCH_TEMPLATE_TEST_CASE( ... ) INTERNAL_CATCH_EXPAND_VARGS( INTERNAL_CATCH_TEMPLATE_TEST_CASE( __VA_ARGS__ ) )
++#define CATCH_TEMPLATE_TEST_CASE_SIG( ... ) INTERNAL_CATCH_EXPAND_VARGS( INTERNAL_CATCH_TEMPLATE_TEST_CASE_SIG( __VA_ARGS__ ) )
+ #define CATCH_TEMPLATE_TEST_CASE_METHOD( className, ... ) INTERNAL_CATCH_EXPAND_VARGS( INTERNAL_CATCH_TEMPLATE_TEST_CASE_METHOD( className, __VA_ARGS__ ) )
++#define CATCH_TEMPLATE_TEST_CASE_METHOD_SIG( className, ... ) INTERNAL_CATCH_EXPAND_VARGS( INTERNAL_CATCH_TEMPLATE_TEST_CASE_METHOD_SIG( className, __VA_ARGS__ ) )
++#define CATCH_TEMPLATE_PRODUCT_TEST_CASE( ... ) INTERNAL_CATCH_EXPAND_VARGS( INTERNAL_CATCH_TEMPLATE_PRODUCT_TEST_CASE( __VA_ARGS__ ) )
++#define CATCH_TEMPLATE_PRODUCT_TEST_CASE_SIG( ... ) INTERNAL_CATCH_EXPAND_VARGS( INTERNAL_CATCH_TEMPLATE_PRODUCT_TEST_CASE_SIG( __VA_ARGS__ ) )
++#define CATCH_TEMPLATE_PRODUCT_TEST_CASE_METHOD( className, ... ) INTERNAL_CATCH_EXPAND_VARGS( INTERNAL_CATCH_TEMPLATE_PRODUCT_TEST_CASE_METHOD( className, __VA_ARGS__ ) )
++#define CATCH_TEMPLATE_PRODUCT_TEST_CASE_METHOD_SIG( className, ... ) INTERNAL_CATCH_EXPAND_VARGS( INTERNAL_CATCH_TEMPLATE_PRODUCT_TEST_CASE_METHOD_SIG( className, __VA_ARGS__ ) )
+ #endif
+ 
+ #if !defined(CATCH_CONFIG_RUNTIME_STATIC_REQUIRE)
+@@ -14106,6 +17653,13 @@
+ #define CATCH_THEN( desc )      INTERNAL_CATCH_DYNAMIC_SECTION( "     Then: " << desc )
+ #define CATCH_AND_THEN( desc )  INTERNAL_CATCH_DYNAMIC_SECTION( "      And: " << desc )
+ 
++#if defined(CATCH_CONFIG_ENABLE_BENCHMARKING)
++#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__,,))
++#define CATCH_BENCHMARK_ADVANCED(name) \
++    INTERNAL_CATCH_BENCHMARK_ADVANCED(INTERNAL_CATCH_UNIQUE_NAME(C_A_T_C_H_B_E_N_C_H_), name)
++#endif // CATCH_CONFIG_ENABLE_BENCHMARKING
++
+ // If CATCH_CONFIG_PREFIX_ALL is not defined then the CATCH_ prefix is not required
+ #else
+ 
+@@ -14141,6 +17695,7 @@
+ #endif // CATCH_CONFIG_DISABLE_MATCHERS
+ 
+ #define INFO( msg ) INTERNAL_CATCH_INFO( "INFO", msg )
++#define UNSCOPED_INFO( msg ) INTERNAL_CATCH_UNSCOPED_INFO( "UNSCOPED_INFO", msg )
+ #define WARN( msg ) INTERNAL_CATCH_MSG( "WARN", Catch::ResultWas::Warning, Catch::ResultDisposition::ContinueOnFailure, msg )
+ #define CAPTURE( ... ) INTERNAL_CATCH_CAPTURE( INTERNAL_CATCH_UNIQUE_NAME(capturer), "CAPTURE",__VA_ARGS__ )
+ 
+@@ -14157,10 +17712,26 @@
+ 
+ #ifndef CATCH_CONFIG_TRADITIONAL_MSVC_PREPROCESSOR
+ #define TEMPLATE_TEST_CASE( ... ) INTERNAL_CATCH_TEMPLATE_TEST_CASE( __VA_ARGS__ )
++#define TEMPLATE_TEST_CASE_SIG( ... ) INTERNAL_CATCH_TEMPLATE_TEST_CASE_SIG( __VA_ARGS__ )
+ #define TEMPLATE_TEST_CASE_METHOD( className, ... ) INTERNAL_CATCH_TEMPLATE_TEST_CASE_METHOD( className, __VA_ARGS__ )
++#define TEMPLATE_TEST_CASE_METHOD_SIG( className, ... ) INTERNAL_CATCH_TEMPLATE_TEST_CASE_METHOD_SIG( className, __VA_ARGS__ )
++#define TEMPLATE_PRODUCT_TEST_CASE( ... ) INTERNAL_CATCH_TEMPLATE_PRODUCT_TEST_CASE( __VA_ARGS__ )
++#define TEMPLATE_PRODUCT_TEST_CASE_SIG( ... ) INTERNAL_CATCH_TEMPLATE_PRODUCT_TEST_CASE_SIG( __VA_ARGS__ )
++#define TEMPLATE_PRODUCT_TEST_CASE_METHOD( className, ... ) INTERNAL_CATCH_TEMPLATE_PRODUCT_TEST_CASE_METHOD( className, __VA_ARGS__ )
++#define TEMPLATE_PRODUCT_TEST_CASE_METHOD_SIG( className, ... ) INTERNAL_CATCH_TEMPLATE_PRODUCT_TEST_CASE_METHOD_SIG( className, __VA_ARGS__ )
++#define TEMPLATE_LIST_TEST_CASE( ... ) INTERNAL_CATCH_TEMPLATE_LIST_TEST_CASE(__VA_ARGS__)
++#define TEMPLATE_LIST_TEST_CASE_METHOD( className, ... ) INTERNAL_CATCH_TEMPLATE_LIST_TEST_CASE_METHOD( className, __VA_ARGS__ )
+ #else
+ #define TEMPLATE_TEST_CASE( ... ) INTERNAL_CATCH_EXPAND_VARGS( INTERNAL_CATCH_TEMPLATE_TEST_CASE( __VA_ARGS__ ) )
++#define TEMPLATE_TEST_CASE_SIG( ... ) INTERNAL_CATCH_EXPAND_VARGS( INTERNAL_CATCH_TEMPLATE_TEST_CASE_SIG( __VA_ARGS__ ) )
+ #define TEMPLATE_TEST_CASE_METHOD( className, ... ) INTERNAL_CATCH_EXPAND_VARGS( INTERNAL_CATCH_TEMPLATE_TEST_CASE_METHOD( className, __VA_ARGS__ ) )
++#define TEMPLATE_TEST_CASE_METHOD_SIG( className, ... ) INTERNAL_CATCH_EXPAND_VARGS( INTERNAL_CATCH_TEMPLATE_TEST_CASE_METHOD_SIG( className, __VA_ARGS__ ) )
++#define TEMPLATE_PRODUCT_TEST_CASE( ... ) INTERNAL_CATCH_EXPAND_VARGS( INTERNAL_CATCH_TEMPLATE_PRODUCT_TEST_CASE( __VA_ARGS__ ) )
++#define TEMPLATE_PRODUCT_TEST_CASE_SIG( ... ) INTERNAL_CATCH_EXPAND_VARGS( INTERNAL_CATCH_TEMPLATE_PRODUCT_TEST_CASE_SIG( __VA_ARGS__ ) )
++#define TEMPLATE_PRODUCT_TEST_CASE_METHOD( className, ... ) INTERNAL_CATCH_EXPAND_VARGS( INTERNAL_CATCH_TEMPLATE_PRODUCT_TEST_CASE_METHOD( className, __VA_ARGS__ ) )
++#define TEMPLATE_PRODUCT_TEST_CASE_METHOD_SIG( className, ... ) INTERNAL_CATCH_EXPAND_VARGS( INTERNAL_CATCH_TEMPLATE_PRODUCT_TEST_CASE_METHOD_SIG( className, __VA_ARGS__ ) )
++#define TEMPLATE_LIST_TEST_CASE( ... ) INTERNAL_CATCH_EXPAND_VARGS( INTERNAL_CATCH_TEMPLATE_LIST_TEST_CASE( __VA_ARGS__ ) )
++#define TEMPLATE_LIST_TEST_CASE_METHOD( className, ... ) INTERNAL_CATCH_EXPAND_VARGS( INTERNAL_CATCH_TEMPLATE_LIST_TEST_CASE_METHOD( className, __VA_ARGS__ ) )
+ #endif
+ 
+ #if !defined(CATCH_CONFIG_RUNTIME_STATIC_REQUIRE)
+@@ -14186,6 +17757,13 @@
+ #define THEN( desc )      INTERNAL_CATCH_DYNAMIC_SECTION( "     Then: " << desc )
+ #define AND_THEN( desc )  INTERNAL_CATCH_DYNAMIC_SECTION( "      And: " << desc )
+ 
++#if defined(CATCH_CONFIG_ENABLE_BENCHMARKING)
++#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__,,))
++#define BENCHMARK_ADVANCED(name) \
++    INTERNAL_CATCH_BENCHMARK_ADVANCED(INTERNAL_CATCH_UNIQUE_NAME(C_A_T_C_H_B_E_N_C_H_), name)
++#endif // CATCH_CONFIG_ENABLE_BENCHMARKING
++
+ using Catch::Detail::Approx;
+ 
+ #else // CATCH_CONFIG_DISABLE
+@@ -14225,12 +17803,13 @@
+ #define CATCH_REQUIRE_THAT( arg, matcher ) (void)(0)
+ #endif // CATCH_CONFIG_DISABLE_MATCHERS
+ 
+-#define CATCH_INFO( msg )    (void)(0)
+-#define CATCH_WARN( msg )    (void)(0)
+-#define CATCH_CAPTURE( msg ) (void)(0)
++#define CATCH_INFO( msg )          (void)(0)
++#define CATCH_UNSCOPED_INFO( msg ) (void)(0)
++#define CATCH_WARN( msg )          (void)(0)
++#define CATCH_CAPTURE( msg )       (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( 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_METHOD_AS_TEST_CASE( method, ... )
+ #define CATCH_REGISTER_TEST_CASE( Function, ... ) (void)(0)
+ #define CATCH_SECTION( ... )
+@@ -14239,19 +17818,31 @@
+ #define CATCH_FAIL_CHECK( ... ) (void)(0)
+ #define CATCH_SUCCEED( ... ) (void)(0)
+ 
+-#define CATCH_ANON_TEST_CASE() INTERNAL_CATCH_TESTCASE_NO_REGISTRATION(INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_S_T____ ))
++#define CATCH_ANON_TEST_CASE() INTERNAL_CATCH_TESTCASE_NO_REGISTRATION(INTERNAL_CATCH_UNIQUE_NAME( C_A_T_C_H_T_E_S_T_ ))
+ 
+ #ifndef CATCH_CONFIG_TRADITIONAL_MSVC_PREPROCESSOR
+-#define CATCH_TEMPLATE_TEST_CASE( ... ) INTERNAL_CATCH_TEMPLATE_TEST_CASE_NO_REGISTRATION(INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_M_P_L_A_T_E____T_E_S_T____ ) )
+-#define CATCH_TEMPLATE_TEST_CASE_METHOD( className, ... ) INTERNAL_CATCH_TEMPLATE_TEST_CASE_METHOD_NO_REGISTRATION(INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_M_P_L_A_T_E____T_E_S_T____ ), className )
++#define CATCH_TEMPLATE_TEST_CASE( ... ) INTERNAL_CATCH_TEMPLATE_TEST_CASE_NO_REGISTRATION(__VA_ARGS__)
++#define CATCH_TEMPLATE_TEST_CASE_SIG( ... ) INTERNAL_CATCH_TEMPLATE_TEST_CASE_SIG_NO_REGISTRATION(__VA_ARGS__)
++#define CATCH_TEMPLATE_TEST_CASE_METHOD( className, ... ) INTERNAL_CATCH_TEMPLATE_TEST_CASE_METHOD_NO_REGISTRATION(className, __VA_ARGS__)
++#define CATCH_TEMPLATE_TEST_CASE_METHOD_SIG( className, ... ) INTERNAL_CATCH_TEMPLATE_TEST_CASE_METHOD_SIG_NO_REGISTRATION(className, __VA_ARGS__ )
++#define CATCH_TEMPLATE_PRODUCT_TEST_CASE( ... ) CATCH_TEMPLATE_TEST_CASE( __VA_ARGS__ )
++#define CATCH_TEMPLATE_PRODUCT_TEST_CASE_SIG( ... ) CATCH_TEMPLATE_TEST_CASE( __VA_ARGS__ )
++#define CATCH_TEMPLATE_PRODUCT_TEST_CASE_METHOD( className, ... ) CATCH_TEMPLATE_TEST_CASE_METHOD( className, __VA_ARGS__ )
++#define CATCH_TEMPLATE_PRODUCT_TEST_CASE_METHOD_SIG( className, ... ) CATCH_TEMPLATE_TEST_CASE_METHOD( className, __VA_ARGS__ )
+ #else
+-#define CATCH_TEMPLATE_TEST_CASE( ... ) INTERNAL_CATCH_EXPAND_VARGS( INTERNAL_CATCH_TEMPLATE_TEST_CASE_NO_REGISTRATION(INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_M_P_L_A_T_E____T_E_S_T____ ) ) )
+-#define CATCH_TEMPLATE_TEST_CASE_METHOD( className, ... ) INTERNAL_CATCH_EXPAND_VARGS( INTERNAL_CATCH_TEMPLATE_TEST_CASE_METHOD_NO_REGISTRATION(INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_M_P_L_A_T_E____T_E_S_T____ ), className ) )
++#define CATCH_TEMPLATE_TEST_CASE( ... ) INTERNAL_CATCH_EXPAND_VARGS( INTERNAL_CATCH_TEMPLATE_TEST_CASE_NO_REGISTRATION(__VA_ARGS__) )
++#define CATCH_TEMPLATE_TEST_CASE_SIG( ... ) INTERNAL_CATCH_EXPAND_VARGS( INTERNAL_CATCH_TEMPLATE_TEST_CASE_SIG_NO_REGISTRATION(__VA_ARGS__) )
++#define CATCH_TEMPLATE_TEST_CASE_METHOD( className, ... ) INTERNAL_CATCH_EXPAND_VARGS( INTERNAL_CATCH_TEMPLATE_TEST_CASE_METHOD_NO_REGISTRATION(className, __VA_ARGS__ ) )
++#define CATCH_TEMPLATE_TEST_CASE_METHOD_SIG( className, ... ) INTERNAL_CATCH_EXPAND_VARGS( INTERNAL_CATCH_TEMPLATE_TEST_CASE_METHOD_SIG_NO_REGISTRATION(className, __VA_ARGS__ ) )
++#define CATCH_TEMPLATE_PRODUCT_TEST_CASE( ... ) CATCH_TEMPLATE_TEST_CASE( __VA_ARGS__ )
++#define CATCH_TEMPLATE_PRODUCT_TEST_CASE_SIG( ... ) CATCH_TEMPLATE_TEST_CASE( __VA_ARGS__ )
++#define CATCH_TEMPLATE_PRODUCT_TEST_CASE_METHOD( className, ... ) CATCH_TEMPLATE_TEST_CASE_METHOD( className, __VA_ARGS__ )
++#define CATCH_TEMPLATE_PRODUCT_TEST_CASE_METHOD_SIG( className, ... ) CATCH_TEMPLATE_TEST_CASE_METHOD( className, __VA_ARGS__ )
+ #endif
+ 
+ // "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( 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_GIVEN( desc )
+ #define CATCH_AND_GIVEN( desc )
+ #define CATCH_WHEN( desc )
+@@ -14297,11 +17888,12 @@
+ #endif // CATCH_CONFIG_DISABLE_MATCHERS
+ 
+ #define INFO( msg ) (void)(0)
++#define UNSCOPED_INFO( msg ) (void)(0)
+ #define WARN( msg ) (void)(0)
+ #define CAPTURE( msg ) (void)(0)
+ 
+-#define TEST_CASE( ... )  INTERNAL_CATCH_TESTCASE_NO_REGISTRATION(INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_S_T____ ))
+-#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( C_A_T_C_H_T_E_S_T_ ))
++#define TEST_CASE_METHOD( className, ... ) INTERNAL_CATCH_TESTCASE_NO_REGISTRATION(INTERNAL_CATCH_UNIQUE_NAME( C_A_T_C_H_T_E_S_T_ ))
+ #define METHOD_AS_TEST_CASE( method, ... )
+ #define REGISTER_TEST_CASE( Function, ... ) (void)(0)
+ #define SECTION( ... )
+@@ -14309,14 +17901,26 @@
+ #define FAIL( ... ) (void)(0)
+ #define FAIL_CHECK( ... ) (void)(0)
+ #define SUCCEED( ... ) (void)(0)
+-#define ANON_TEST_CASE() INTERNAL_CATCH_TESTCASE_NO_REGISTRATION(INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_S_T____ ))
++#define ANON_TEST_CASE() INTERNAL_CATCH_TESTCASE_NO_REGISTRATION(INTERNAL_CATCH_UNIQUE_NAME( C_A_T_C_H_T_E_S_T_ ))
+ 
+ #ifndef CATCH_CONFIG_TRADITIONAL_MSVC_PREPROCESSOR
+-#define TEMPLATE_TEST_CASE( ... ) INTERNAL_CATCH_TEMPLATE_TEST_CASE_NO_REGISTRATION(INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_M_P_L_A_T_E____T_E_S_T____ ) )
+-#define TEMPLATE_TEST_CASE_METHOD( className, ... ) INTERNAL_CATCH_TEMPLATE_TEST_CASE_METHOD_NO_REGISTRATION(INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_M_P_L_A_T_E____T_E_S_T____ ), className )
++#define TEMPLATE_TEST_CASE( ... ) INTERNAL_CATCH_TEMPLATE_TEST_CASE_NO_REGISTRATION(__VA_ARGS__)
++#define TEMPLATE_TEST_CASE_SIG( ... ) INTERNAL_CATCH_TEMPLATE_TEST_CASE_SIG_NO_REGISTRATION(__VA_ARGS__)
++#define TEMPLATE_TEST_CASE_METHOD( className, ... ) INTERNAL_CATCH_TEMPLATE_TEST_CASE_METHOD_NO_REGISTRATION(className, __VA_ARGS__)
++#define TEMPLATE_TEST_CASE_METHOD_SIG( className, ... ) INTERNAL_CATCH_TEMPLATE_TEST_CASE_METHOD_SIG_NO_REGISTRATION(className, __VA_ARGS__ )
++#define TEMPLATE_PRODUCT_TEST_CASE( ... ) TEMPLATE_TEST_CASE( __VA_ARGS__ )
++#define TEMPLATE_PRODUCT_TEST_CASE_SIG( ... ) TEMPLATE_TEST_CASE( __VA_ARGS__ )
++#define TEMPLATE_PRODUCT_TEST_CASE_METHOD( className, ... ) TEMPLATE_TEST_CASE_METHOD( className, __VA_ARGS__ )
++#define TEMPLATE_PRODUCT_TEST_CASE_METHOD_SIG( className, ... ) TEMPLATE_TEST_CASE_METHOD( className, __VA_ARGS__ )
+ #else
+-#define TEMPLATE_TEST_CASE( ... ) INTERNAL_CATCH_EXPAND_VARGS( INTERNAL_CATCH_TEMPLATE_TEST_CASE_NO_REGISTRATION(INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_M_P_L_A_T_E____T_E_S_T____ ) ) )
+-#define TEMPLATE_TEST_CASE_METHOD( className, ... ) INTERNAL_CATCH_EXPAND_VARGS( INTERNAL_CATCH_TEMPLATE_TEST_CASE_METHOD_NO_REGISTRATION(INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_M_P_L_A_T_E____T_E_S_T____ ), className ) )
++#define TEMPLATE_TEST_CASE( ... ) INTERNAL_CATCH_EXPAND_VARGS( INTERNAL_CATCH_TEMPLATE_TEST_CASE_NO_REGISTRATION(__VA_ARGS__) )
++#define TEMPLATE_TEST_CASE_SIG( ... ) INTERNAL_CATCH_EXPAND_VARGS( INTERNAL_CATCH_TEMPLATE_TEST_CASE_SIG_NO_REGISTRATION(__VA_ARGS__) )
++#define TEMPLATE_TEST_CASE_METHOD( className, ... ) INTERNAL_CATCH_EXPAND_VARGS( INTERNAL_CATCH_TEMPLATE_TEST_CASE_METHOD_NO_REGISTRATION(className, __VA_ARGS__ ) )
++#define TEMPLATE_TEST_CASE_METHOD_SIG( className, ... ) INTERNAL_CATCH_EXPAND_VARGS( INTERNAL_CATCH_TEMPLATE_TEST_CASE_METHOD_SIG_NO_REGISTRATION(className, __VA_ARGS__ ) )
++#define TEMPLATE_PRODUCT_TEST_CASE( ... ) TEMPLATE_TEST_CASE( __VA_ARGS__ )
++#define TEMPLATE_PRODUCT_TEST_CASE_SIG( ... ) TEMPLATE_TEST_CASE( __VA_ARGS__ )
++#define TEMPLATE_PRODUCT_TEST_CASE_METHOD( className, ... ) TEMPLATE_TEST_CASE_METHOD( className, __VA_ARGS__ )
++#define TEMPLATE_PRODUCT_TEST_CASE_METHOD_SIG( className, ... ) TEMPLATE_TEST_CASE_METHOD( className, __VA_ARGS__ )
+ #endif
+ 
+ #define STATIC_REQUIRE( ... )       (void)(0)
+@@ -14327,8 +17931,8 @@
+ #define CATCH_TRANSLATE_EXCEPTION( signature ) INTERNAL_CATCH_TRANSLATE_EXCEPTION_NO_REG( INTERNAL_CATCH_UNIQUE_NAME( catch_internal_ExceptionTranslator ), signature )
+ 
+ // "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( 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 GIVEN( desc )
+ #define AND_GIVEN( desc )


More information about the Debian-med-packaging mailing list