From 63fe71269db8e6485308cb1b3b1a89fe4c259fc4 Mon Sep 17 00:00:00 2001
From: Francois <thiebolt@irit.fr>
Date: Thu, 25 May 2023 20:41:27 +0200
Subject: [PATCH] update

---
 neosensor/libraries/ArduinoJson/ArduinoJson.h |     2 +-
 neosensor/libraries/ArduinoJson/CHANGELOG.md  |    27 +
 .../libraries/ArduinoJson/CMakeLists.txt      |    12 +-
 .../ArduinoJson/{LICENSE.md => LICENSE.txt}   |    20 +-
 neosensor/libraries/ArduinoJson/README.md     |    23 +-
 neosensor/libraries/ArduinoJson/appveyor.yml  |    11 +-
 .../JsonConfigFile/JsonConfigFile.ino         |     2 +-
 .../JsonFilterExample/JsonFilterExample.ino   |     2 +-
 .../JsonGeneratorExample.ino                  |     2 +-
 .../JsonHttpClient/JsonHttpClient.ino         |     2 +-
 .../JsonParserExample/JsonParserExample.ino   |     2 +-
 .../examples/JsonServer/JsonServer.ino        |     2 +-
 .../examples/JsonUdpBeacon/JsonUdpBeacon.ino  |     2 +-
 .../examples/MsgPackParser/MsgPackParser.ino  |     2 +-
 .../ProgmemExample/ProgmemExample.ino         |     2 +-
 .../examples/StringExample/StringExample.ino  |     2 +-
 .../ArduinoJson/extras/CompileOptions.cmake   |    18 +-
 .../extras/ci/espidf/CMakeLists.txt           |     2 +-
 .../extras/ci/espidf/main/CMakeLists.txt      |     8 +-
 .../extras/ci/espidf/main/main.cpp            |     2 +-
 .../ArduinoJson/extras/conf_test/avr.cpp      |     4 +-
 .../ArduinoJson/extras/conf_test/esp8266.cpp  |     2 +-
 .../ArduinoJson/extras/conf_test/x64.cpp      |     2 +-
 .../ArduinoJson/extras/conf_test/x86.cpp      |     2 +-
 .../ArduinoJson/extras/fuzzing/CMakeLists.txt |    25 +-
 .../ArduinoJson/extras/fuzzing/Makefile       |     2 +-
 .../ArduinoJson/extras/fuzzing/reproducer.cpp |     2 +-
 .../extras/scripts/build-arduino-package.sh   |     2 +-
 .../extras/scripts/build-single-header.sh     |     3 +-
 .../scripts/publish-particle-library.sh       |     6 +-
 .../ArduinoJson/extras/scripts/publish.sh     |     5 +-
 .../scripts/wandbox/JsonGeneratorExample.cpp  |     2 +-
 .../scripts/wandbox/JsonParserExample.cpp     |     2 +-
 .../scripts/wandbox/MsgPackParserExample.cpp  |     2 +-
 .../ArduinoJson/extras/tests/CMakeLists.txt   |     5 +-
 .../extras/tests/Cpp11/CMakeLists.txt         |    36 -
 .../extras/tests/Cpp11/stl_containers.cpp     |    72 -
 .../extras/tests/Cpp17/CMakeLists.txt         |     4 +-
 .../extras/tests/Cpp17/string_view.cpp        |     4 +-
 .../extras/tests/Cpp20/CMakeLists.txt         |     4 +-
 .../extras/tests/FailingBuilds/CMakeLists.txt |    24 +-
 .../extras/tests/FailingBuilds/Issue1189.cpp  |     2 +-
 .../extras/tests/FailingBuilds/Issue978.cpp   |     2 +-
 .../tests/FailingBuilds/assign_char.cpp       |     2 +-
 .../FailingBuilds/delete_jsondocument.cpp     |     2 +-
 .../tests/FailingBuilds/read_long_long.cpp    |     6 +-
 .../tests/FailingBuilds/variant_as_char.cpp   |     2 +-
 .../tests/FailingBuilds/write_long_long.cpp   |     6 +-
 .../extras/tests/Helpers/Arduino.h            |     5 +-
 .../extras/tests/Helpers/CustomReader.hpp     |    16 +-
 .../extras/tests/Helpers/api/Print.h          |     2 +-
 .../extras/tests/Helpers/api/Stream.h         |     2 +-
 .../extras/tests/Helpers/api/String.h         |     2 +-
 .../{progmem_emulation.hpp => avr/pgmspace.h} |    10 +-
 .../tests/IntegrationTests/CMakeLists.txt     |     8 +-
 .../tests/IntegrationTests/gbathree.cpp       |     2 +-
 .../tests/IntegrationTests/issue772.cpp       |     2 +-
 .../tests/IntegrationTests/openweathermap.cpp |     2 +-
 .../tests/IntegrationTests/round_trip.cpp     |     2 +-
 .../extras/tests/JsonArray/CMakeLists.txt     |     4 +-
 .../extras/tests/JsonArray/add.cpp            |     2 +-
 .../extras/tests/JsonArray/clear.cpp          |     2 +-
 .../extras/tests/JsonArray/compare.cpp        |     2 +-
 .../extras/tests/JsonArray/copyArray.cpp      |     4 +-
 .../extras/tests/JsonArray/createNested.cpp   |     2 +-
 .../extras/tests/JsonArray/equals.cpp         |     2 +-
 .../extras/tests/JsonArray/isNull.cpp         |     2 +-
 .../extras/tests/JsonArray/iterator.cpp       |     2 +-
 .../extras/tests/JsonArray/memoryUsage.cpp    |     2 +-
 .../extras/tests/JsonArray/nesting.cpp        |     2 +-
 .../extras/tests/JsonArray/remove.cpp         |     2 +-
 .../extras/tests/JsonArray/size.cpp           |     2 +-
 .../extras/tests/JsonArray/std_string.cpp     |     2 +-
 .../extras/tests/JsonArray/subscript.cpp      |     2 +-
 .../extras/tests/JsonArray/unbound.cpp        |     2 +-
 .../tests/JsonDeserializer/CMakeLists.txt     |     4 +-
 .../JsonDeserializer/DeserializationError.cpp |     4 +-
 .../extras/tests/JsonDeserializer/array.cpp   |     2 +-
 .../tests/JsonDeserializer/array_static.cpp   |     2 +-
 .../extras/tests/JsonDeserializer/filter.cpp  |     2 +-
 .../JsonDeserializer/incomplete_input.cpp     |     2 +-
 .../tests/JsonDeserializer/input_types.cpp    |    26 +-
 .../tests/JsonDeserializer/invalid_input.cpp  |     2 +-
 .../extras/tests/JsonDeserializer/misc.cpp    |     2 +-
 .../tests/JsonDeserializer/nestingLimit.cpp   |     4 +-
 .../extras/tests/JsonDeserializer/number.cpp  |     6 +-
 .../extras/tests/JsonDeserializer/object.cpp  |     8 +-
 .../tests/JsonDeserializer/object_static.cpp  |     2 +-
 .../extras/tests/JsonDeserializer/string.cpp  |     2 +-
 .../tests/JsonDocument/BasicJsonDocument.cpp  |    29 +-
 .../extras/tests/JsonDocument/CMakeLists.txt  |     5 +-
 .../JsonDocument/DynamicJsonDocument.cpp      |     4 +-
 .../tests/JsonDocument/ElementProxy.cpp       |     4 +-
 .../extras/tests/JsonDocument/MemberProxy.cpp |     4 +-
 .../tests/JsonDocument/StaticJsonDocument.cpp |     2 +-
 .../extras/tests/JsonDocument/add.cpp         |     2 +-
 .../extras/tests/JsonDocument/cast.cpp        |     2 +-
 .../extras/tests/JsonDocument/compare.cpp     |     2 +-
 .../extras/tests/JsonDocument/containsKey.cpp |     2 +-
 .../tests/JsonDocument/createNested.cpp       |     2 +-
 .../extras/tests/JsonDocument/isNull.cpp      |     2 +-
 .../{Cpp11 => JsonDocument}/issue1120.cpp     |     0
 .../extras/tests/JsonDocument/nesting.cpp     |     2 +-
 .../extras/tests/JsonDocument/overflowed.cpp  |     2 +-
 .../extras/tests/JsonDocument/remove.cpp      |     2 +-
 .../extras/tests/JsonDocument/shrinkToFit.cpp |    32 +-
 .../extras/tests/JsonDocument/size.cpp        |     2 +-
 .../extras/tests/JsonDocument/subscript.cpp   |     2 +-
 .../extras/tests/JsonObject/CMakeLists.txt    |     4 +-
 .../extras/tests/JsonObject/clear.cpp         |     2 +-
 .../extras/tests/JsonObject/compare.cpp       |     2 +-
 .../extras/tests/JsonObject/containsKey.cpp   |     2 +-
 .../extras/tests/JsonObject/copy.cpp          |     2 +-
 .../tests/JsonObject/createNestedArray.cpp    |     2 +-
 .../tests/JsonObject/createNestedObject.cpp   |     2 +-
 .../extras/tests/JsonObject/equals.cpp        |     2 +-
 .../extras/tests/JsonObject/invalid.cpp       |     2 +-
 .../extras/tests/JsonObject/isNull.cpp        |     2 +-
 .../extras/tests/JsonObject/iterator.cpp      |     2 +-
 .../extras/tests/JsonObject/memoryUsage.cpp   |     2 +-
 .../extras/tests/JsonObject/nesting.cpp       |     2 +-
 .../extras/tests/JsonObject/remove.cpp        |     2 +-
 .../extras/tests/JsonObject/size.cpp          |     2 +-
 .../extras/tests/JsonObject/std_string.cpp    |     2 +-
 .../extras/tests/JsonObject/subscript.cpp     |     2 +-
 .../tests/JsonSerializer/CMakeLists.txt       |     4 +-
 .../tests/JsonSerializer/CustomWriter.cpp     |    15 +-
 .../extras/tests/JsonSerializer/JsonArray.cpp |     2 +-
 .../tests/JsonSerializer/JsonArrayPretty.cpp  |     2 +-
 .../tests/JsonSerializer/JsonObject.cpp       |     2 +-
 .../tests/JsonSerializer/JsonObjectPretty.cpp |     2 +-
 .../tests/JsonSerializer/JsonVariant.cpp      |     2 +-
 .../tests/JsonSerializer/std_stream.cpp       |     2 +-
 .../tests/JsonSerializer/std_string.cpp       |     2 +-
 .../extras/tests/JsonVariant/CMakeLists.txt   |     6 +-
 .../extras/tests/JsonVariant/add.cpp          |     2 +-
 .../extras/tests/JsonVariant/as.cpp           |     4 +-
 .../extras/tests/JsonVariant/clear.cpp        |     2 +-
 .../extras/tests/JsonVariant/compare.cpp      |     2 +-
 .../extras/tests/JsonVariant/containsKey.cpp  |     4 +-
 .../extras/tests/JsonVariant/converters.cpp   |    16 +-
 .../extras/tests/JsonVariant/copy.cpp         |     2 +-
 .../extras/tests/JsonVariant/createNested.cpp |     2 +-
 .../extras/tests/JsonVariant/is.cpp           |     2 +-
 .../extras/tests/JsonVariant/isnull.cpp       |     2 +-
 .../extras/tests/JsonVariant/memoryUsage.cpp  |     2 +-
 .../extras/tests/JsonVariant/misc.cpp         |     7 +-
 .../extras/tests/JsonVariant/nesting.cpp      |     2 +-
 .../tests/{Cpp11 => JsonVariant}/nullptr.cpp  |    12 +-
 .../extras/tests/JsonVariant/or.cpp           |     2 +-
 .../extras/tests/JsonVariant/overflow.cpp     |     2 +-
 .../extras/tests/JsonVariant/remove.cpp       |     4 +-
 .../extras/tests/JsonVariant/set.cpp          |     2 +-
 .../extras/tests/JsonVariant/shallowCopy.cpp  |     2 +-
 .../extras/tests/JsonVariant/size.cpp         |     2 +-
 .../tests/JsonVariant/stl_containers.cpp      |   138 +
 .../extras/tests/JsonVariant/subscript.cpp    |     2 +-
 .../extras/tests/JsonVariant/types.cpp        |     2 +-
 .../extras/tests/JsonVariant/unbound.cpp      |     2 +-
 .../extras/tests/MemoryPool/CMakeLists.txt    |     6 +-
 .../extras/tests/MemoryPool/StringCopier.cpp  |     4 +-
 .../extras/tests/MemoryPool/allocVariant.cpp  |     4 +-
 .../extras/tests/MemoryPool/clear.cpp         |     4 +-
 .../extras/tests/MemoryPool/saveString.cpp    |     4 +-
 .../extras/tests/MemoryPool/size.cpp          |     4 +-
 .../extras/tests/Misc/CMakeLists.txt          |     4 +-
 .../extras/tests/Misc/FloatParts.cpp          |     4 +-
 .../extras/tests/Misc/JsonString.cpp          |     2 +-
 .../extras/tests/Misc/NoArduinoHeader.cpp     |     2 +-
 .../ArduinoJson/extras/tests/Misc/Readers.cpp |    16 +-
 .../extras/tests/Misc/StringAdapters.cpp      |    19 +-
 .../extras/tests/Misc/StringWriter.cpp        |    11 +-
 .../extras/tests/Misc/TypeTraits.cpp          |    65 +-
 .../ArduinoJson/extras/tests/Misc/Utf16.cpp   |     4 +-
 .../ArduinoJson/extras/tests/Misc/Utf8.cpp    |     4 +-
 .../extras/tests/Misc/arithmeticCompare.cpp   |   104 +-
 .../extras/tests/Misc/conflicts.cpp           |     5 +-
 .../extras/tests/Misc/custom_string.hpp       |     2 +-
 .../extras/tests/Misc/printable.cpp           |    14 +-
 .../extras/tests/Misc/unsigned_char.cpp       |     2 +-
 .../ArduinoJson/extras/tests/Misc/version.cpp |     2 +-
 .../extras/tests/Misc/weird_strcmp.hpp        |     8 +-
 .../tests/MixedConfiguration/CMakeLists.txt   |     6 +-
 .../MixedConfiguration/enable_alignment_0.cpp |     4 +-
 .../MixedConfiguration/enable_alignment_1.cpp |     2 +-
 .../MixedConfiguration/enable_comments_0.cpp  |     2 +-
 .../MixedConfiguration/enable_comments_1.cpp  |     2 +-
 .../MixedConfiguration/enable_infinity_1.cpp  |     2 +-
 .../tests/MixedConfiguration/enable_nan_1.cpp |     2 +-
 .../MixedConfiguration/enable_progmem_1.cpp   |     8 +-
 .../enable_string_deduplication_0.cpp         |     2 +-
 .../enable_string_deduplication_1.cpp         |     2 +-
 .../tests/MixedConfiguration/issue1707.cpp    |     2 +-
 .../use_long_long_0.cpp                       |     0
 .../use_long_long_1.cpp                       |     0
 .../tests/MsgPackDeserializer/CMakeLists.txt  |     4 +-
 .../MsgPackDeserializer/deserializeArray.cpp  |     2 +-
 .../MsgPackDeserializer/deserializeObject.cpp |     2 +-
 .../deserializeStaticVariant.cpp              |     2 +-
 .../deserializeVariant.cpp                    |     2 +-
 .../MsgPackDeserializer/doubleToFloat.cpp     |     4 +-
 .../tests/MsgPackDeserializer/filter.cpp      |     6 +-
 .../MsgPackDeserializer/incompleteInput.cpp   |     2 +-
 .../tests/MsgPackDeserializer/input_types.cpp |     2 +-
 .../extras/tests/MsgPackDeserializer/misc.cpp |     4 +-
 .../MsgPackDeserializer/nestingLimit.cpp      |     4 +-
 .../MsgPackDeserializer/notSupported.cpp      |     2 +-
 .../tests/MsgPackSerializer/CMakeLists.txt    |     4 +-
 .../MsgPackSerializer/destination_types.cpp   |     2 +-
 .../tests/MsgPackSerializer/measure.cpp       |     2 +-
 .../MsgPackSerializer/serializeArray.cpp      |     2 +-
 .../MsgPackSerializer/serializeObject.cpp     |     2 +-
 .../MsgPackSerializer/serializeVariant.cpp    |     2 +-
 .../extras/tests/Numbers/CMakeLists.txt       |     8 +-
 .../extras/tests/Numbers/convertNumber.cpp    |   143 +-
 .../extras/tests/Numbers/parseDouble.cpp      |     4 +-
 .../extras/tests/Numbers/parseFloat.cpp       |     4 +-
 .../extras/tests/Numbers/parseInteger.cpp     |     4 +-
 .../extras/tests/Numbers/parseNumber.cpp      |    10 +-
 .../extras/tests/TextFormatter/CMakeLists.txt |     6 +-
 .../extras/tests/TextFormatter/writeFloat.cpp |     6 +-
 .../tests/TextFormatter/writeInteger.cpp      |     4 +-
 .../tests/TextFormatter/writeString.cpp       |     4 +-
 .../extras/tests/catch/CMakeLists.txt         |    10 +-
 .../ArduinoJson/extras/tests/catch/catch.hpp  | 24335 ++++++++++------
 .../libraries/ArduinoJson/idf_component.yml   |    13 +
 neosensor/libraries/ArduinoJson/library.json  |     3 +-
 .../libraries/ArduinoJson/library.properties  |     2 +-
 neosensor/libraries/ArduinoJson/logo.svg      |     1 -
 .../libraries/ArduinoJson/src/ArduinoJson.h   |     2 +-
 .../libraries/ArduinoJson/src/ArduinoJson.hpp |    42 +-
 .../src/ArduinoJson/Array/ElementProxy.hpp    |    26 +-
 .../src/ArduinoJson/Array/JsonArray.hpp       |    91 +-
 .../src/ArduinoJson/Array/JsonArrayConst.hpp  |    49 +-
 .../src/ArduinoJson/Array/JsonArrayImpl.hpp   |    10 +-
 .../ArduinoJson/Array/JsonArrayIterator.hpp   |    66 +-
 .../src/ArduinoJson/Array/Utilities.hpp       |    29 +-
 .../ArduinoJson/Collection/CollectionData.hpp |    13 +-
 .../ArduinoJson/Collection/CollectionImpl.hpp |    48 +-
 .../src/ArduinoJson/Configuration.hpp         |    37 +-
 .../Deserialization/DeserializationError.hpp  |    59 +-
 .../DeserializationOptions.hpp                |    35 +
 .../ArduinoJson/Deserialization/Filter.hpp    |    28 +-
 .../Deserialization/NestingLimit.hpp          |    21 +-
 .../ArduinoJson/Deserialization/Reader.hpp    |    31 +-
 .../Readers/ArduinoStreamReader.hpp           |    16 +-
 .../Readers/ArduinoStringReader.hpp           |     8 +-
 .../Deserialization/Readers/FlashReader.hpp   |    35 +-
 .../Readers/IteratorReader.hpp                |    19 +-
 .../Deserialization/Readers/RamReader.hpp     |    14 +-
 .../Readers/StdStreamReader.hpp               |    17 +-
 .../Deserialization/Readers/VariantReader.hpp |     6 +-
 .../Deserialization/deserialize.hpp           |    81 +-
 .../Document/BasicJsonDocument.hpp            |    68 +-
 .../Document/DynamicJsonDocument.hpp          |     6 +-
 .../src/ArduinoJson/Document/JsonDocument.hpp |   135 +-
 .../Document/StaticJsonDocument.hpp           |    23 +-
 .../src/ArduinoJson/Json/EscapeSequence.hpp   |     7 +-
 .../src/ArduinoJson/Json/JsonDeserializer.hpp |   204 +-
 .../src/ArduinoJson/Json/JsonSerializer.hpp   |    45 +-
 .../src/ArduinoJson/Json/Latch.hpp            |    36 +-
 .../ArduinoJson/Json/PrettyJsonSerializer.hpp |    27 +-
 .../src/ArduinoJson/Json/TextFormatter.hpp    |    28 +-
 .../src/ArduinoJson/Json/Utf16.hpp            |    22 +-
 .../ArduinoJson/src/ArduinoJson/Json/Utf8.hpp |     6 +-
 .../src/ArduinoJson/Memory/Alignment.hpp      |     6 +-
 .../src/ArduinoJson/Memory/MemoryPool.hpp     |   114 +-
 .../src/ArduinoJson/Misc/SafeBoolIdiom.hpp    |    26 -
 .../src/ArduinoJson/Misc/SerializedValue.hpp  |    31 +-
 .../MsgPack/MsgPackDeserializer.hpp           |   211 +-
 .../ArduinoJson/MsgPack/MsgPackSerializer.hpp |    23 +-
 .../src/ArduinoJson/MsgPack/endianess.hpp     |     6 +-
 .../src/ArduinoJson/MsgPack/ieee754.hpp       |     6 +-
 .../ArduinoJson/src/ArduinoJson/Namespace.hpp |    33 +-
 .../src/ArduinoJson/Numbers/FloatParts.hpp    |    15 +-
 .../src/ArduinoJson/Numbers/FloatTraits.hpp   |   228 +-
 .../src/ArduinoJson/Numbers/JsonFloat.hpp     |     7 +-
 .../src/ArduinoJson/Numbers/JsonInteger.hpp   |    20 +-
 .../ArduinoJson/Numbers/arithmeticCompare.hpp |    11 +-
 .../src/ArduinoJson/Numbers/convertNumber.hpp |    14 +-
 .../src/ArduinoJson/Numbers/parseNumber.hpp   |     9 +-
 .../src/ArduinoJson/Object/JsonObject.hpp     |   125 +-
 .../ArduinoJson/Object/JsonObjectConst.hpp    |    72 +-
 .../src/ArduinoJson/Object/JsonObjectImpl.hpp |    14 +-
 .../ArduinoJson/Object/JsonObjectIterator.hpp |    66 +-
 .../src/ArduinoJson/Object/JsonPair.hpp       |    35 +-
 .../src/ArduinoJson/Object/MemberProxy.hpp    |    30 +-
 .../src/ArduinoJson/Polyfills/alias_cast.hpp  |     7 +-
 .../src/ArduinoJson/Polyfills/assert.hpp      |     2 +-
 .../src/ArduinoJson/Polyfills/attributes.hpp  |     8 +-
 .../src/ArduinoJson/Polyfills/ctype.hpp       |     7 +-
 .../src/ArduinoJson/Polyfills/integer.hpp     |     7 +-
 .../src/ArduinoJson/Polyfills/limits.hpp      |     6 +-
 .../src/ArduinoJson/Polyfills/math.hpp        |     6 +-
 .../src/ArduinoJson/Polyfills/mpl/max.hpp     |     7 +-
 .../src/ArduinoJson/Polyfills/pgmspace.hpp    |    52 +-
 .../Polyfills/pgmspace_generic.hpp            |    63 +-
 .../ArduinoJson/Polyfills/preprocessor.hpp    |    46 +-
 .../ArduinoJson/Polyfills/static_array.hpp    |    30 -
 .../src/ArduinoJson/Polyfills/type_traits.hpp |     2 +-
 .../Polyfills/type_traits/conditional.hpp     |     7 +-
 .../Polyfills/type_traits/declval.hpp         |     8 +-
 .../Polyfills/type_traits/enable_if.hpp       |     7 +-
 .../type_traits/integral_constant.hpp         |     6 +-
 .../Polyfills/type_traits/is_array.hpp        |     7 +-
 .../Polyfills/type_traits/is_base_of.hpp      |    12 +-
 .../Polyfills/type_traits/is_class.hpp        |     6 +-
 .../Polyfills/type_traits/is_const.hpp        |     7 +-
 .../Polyfills/type_traits/is_convertible.hpp  |    10 +-
 .../Polyfills/type_traits/is_enum.hpp         |     6 +-
 .../type_traits/is_floating_point.hpp         |     6 +-
 .../Polyfills/type_traits/is_integral.hpp     |    13 +-
 .../Polyfills/type_traits/is_pointer.hpp      |     7 +-
 .../Polyfills/type_traits/is_same.hpp         |     7 +-
 .../Polyfills/type_traits/is_signed.hpp       |    14 +-
 .../Polyfills/type_traits/is_unsigned.hpp     |    12 +-
 .../Polyfills/type_traits/make_unsigned.hpp   |    14 +-
 .../Polyfills/type_traits/make_void.hpp       |     6 +-
 .../Polyfills/type_traits/remove_const.hpp    |     7 +-
 .../Polyfills/type_traits/remove_cv.hpp       |     7 +-
 .../type_traits/remove_reference.hpp          |     7 +-
 .../Polyfills/type_traits/type_identity.hpp   |     7 +-
 .../src/ArduinoJson/Polyfills/utility.hpp     |    16 +
 .../Serialization/CountingDecorator.hpp       |    18 +-
 .../src/ArduinoJson/Serialization/Writer.hpp  |    14 +-
 .../Writers/ArduinoStringWriter.hpp           |    32 +-
 .../Serialization/Writers/DummyWriter.hpp     |     7 +-
 .../Serialization/Writers/PrintWriter.hpp     |    16 +-
 .../Writers/StaticStringWriter.hpp            |     7 +-
 .../Serialization/Writers/StdStreamWriter.hpp |    15 +-
 .../Serialization/Writers/StdStringWriter.hpp |    25 +-
 .../src/ArduinoJson/Serialization/measure.hpp |     8 +-
 .../ArduinoJson/Serialization/serialize.hpp   |    19 +-
 .../StringStorage/StringCopier.hpp            |    47 +-
 .../ArduinoJson/StringStorage/StringMover.hpp |    25 +-
 .../StringStorage/StringStorage.hpp           |     6 +-
 .../Strings/Adapters/ArduinoString.hpp        |    25 -
 .../Strings/Adapters/FlashString.hpp          |    30 +-
 .../Strings/Adapters/JsonString.hpp           |    12 +-
 .../Strings/Adapters/RamString.hpp            |    38 +-
 .../Strings/Adapters/StdString.hpp            |    23 -
 .../Strings/Adapters/StringObject.hpp         |    51 +
 .../Strings/Adapters/StringView.hpp           |    22 -
 .../src/ArduinoJson/Strings/IsString.hpp      |     6 +-
 .../src/ArduinoJson/Strings/JsonString.hpp    |    46 +-
 .../src/ArduinoJson/Strings/StoragePolicy.hpp |     6 +-
 .../src/ArduinoJson/Strings/StringAdapter.hpp |     6 +-
 .../ArduinoJson/Strings/StringAdapters.hpp    |    19 +-
 .../src/ArduinoJson/Strings/StringTraits.hpp  |    79 +
 .../src/ArduinoJson/Variant/Converter.hpp     |    10 +-
 .../src/ArduinoJson/Variant/ConverterImpl.hpp |   149 +-
 .../src/ArduinoJson/Variant/JsonVariant.hpp   |    45 +-
 .../ArduinoJson/Variant/JsonVariantConst.hpp  |    81 +-
 .../src/ArduinoJson/Variant/SlotFunctions.hpp |    14 +-
 .../ArduinoJson/Variant/VariantAttorney.hpp   |     6 +-
 .../ArduinoJson/Variant/VariantCompare.hpp    |    36 +-
 .../ArduinoJson/Variant/VariantContent.hpp    |     7 +-
 .../src/ArduinoJson/Variant/VariantData.hpp   |   143 +-
 .../ArduinoJson/Variant/VariantFunctions.hpp  |     6 +-
 .../src/ArduinoJson/Variant/VariantImpl.hpp   |    44 +-
 .../ArduinoJson/Variant/VariantOperators.hpp  |    11 +-
 .../ArduinoJson/Variant/VariantRefBase.hpp    |    31 +-
 .../src/ArduinoJson/Variant/VariantSlot.hpp   |    56 +-
 .../src/ArduinoJson/Variant/VariantTag.hpp    |     6 +-
 .../src/ArduinoJson/Variant/VariantTo.hpp     |     8 +-
 .../src/ArduinoJson/Variant/Visitor.hpp       |     6 +-
 .../src/ArduinoJson/compatibility.hpp         |     4 +-
 .../ArduinoJson/src/ArduinoJson/version.hpp   |     8 +-
 .../libraries/ArduinoJson/src/CMakeLists.txt  |    97 +-
 neosensor/neosensor.ino                       |    33 +-
 370 files changed, 18240 insertions(+), 11962 deletions(-)
 rename neosensor/libraries/ArduinoJson/{LICENSE.md => LICENSE.txt} (95%)
 delete mode 100644 neosensor/libraries/ArduinoJson/extras/tests/Cpp11/CMakeLists.txt
 delete mode 100644 neosensor/libraries/ArduinoJson/extras/tests/Cpp11/stl_containers.cpp
 rename neosensor/libraries/ArduinoJson/extras/tests/Helpers/{progmem_emulation.hpp => avr/pgmspace.h} (76%)
 rename neosensor/libraries/ArduinoJson/extras/tests/{Cpp11 => JsonDocument}/issue1120.cpp (100%)
 rename neosensor/libraries/ArduinoJson/extras/tests/{Cpp11 => JsonVariant}/nullptr.cpp (75%)
 create mode 100644 neosensor/libraries/ArduinoJson/extras/tests/JsonVariant/stl_containers.cpp
 rename neosensor/libraries/ArduinoJson/extras/tests/{Cpp11 => MixedConfiguration}/use_long_long_0.cpp (100%)
 rename neosensor/libraries/ArduinoJson/extras/tests/{Cpp11 => MixedConfiguration}/use_long_long_1.cpp (100%)
 create mode 100644 neosensor/libraries/ArduinoJson/idf_component.yml
 delete mode 100644 neosensor/libraries/ArduinoJson/logo.svg
 create mode 100644 neosensor/libraries/ArduinoJson/src/ArduinoJson/Deserialization/DeserializationOptions.hpp
 delete mode 100644 neosensor/libraries/ArduinoJson/src/ArduinoJson/Misc/SafeBoolIdiom.hpp
 delete mode 100644 neosensor/libraries/ArduinoJson/src/ArduinoJson/Polyfills/static_array.hpp
 create mode 100644 neosensor/libraries/ArduinoJson/src/ArduinoJson/Polyfills/utility.hpp
 delete mode 100644 neosensor/libraries/ArduinoJson/src/ArduinoJson/Strings/Adapters/ArduinoString.hpp
 delete mode 100644 neosensor/libraries/ArduinoJson/src/ArduinoJson/Strings/Adapters/StdString.hpp
 create mode 100644 neosensor/libraries/ArduinoJson/src/ArduinoJson/Strings/Adapters/StringObject.hpp
 delete mode 100644 neosensor/libraries/ArduinoJson/src/ArduinoJson/Strings/Adapters/StringView.hpp
 create mode 100644 neosensor/libraries/ArduinoJson/src/ArduinoJson/Strings/StringTraits.hpp

diff --git a/neosensor/libraries/ArduinoJson/ArduinoJson.h b/neosensor/libraries/ArduinoJson/ArduinoJson.h
index 7e3afa58..15c218f4 100644
--- a/neosensor/libraries/ArduinoJson/ArduinoJson.h
+++ b/neosensor/libraries/ArduinoJson/ArduinoJson.h
@@ -1,5 +1,5 @@
 // ArduinoJson - https://arduinojson.org
-// Copyright © 2014-2022, Benoit BLANCHON
+// Copyright © 2014-2023, Benoit BLANCHON
 // MIT License
 
 #include "src/ArduinoJson.h"
diff --git a/neosensor/libraries/ArduinoJson/CHANGELOG.md b/neosensor/libraries/ArduinoJson/CHANGELOG.md
index a0125a95..f5a9bb0a 100644
--- a/neosensor/libraries/ArduinoJson/CHANGELOG.md
+++ b/neosensor/libraries/ArduinoJson/CHANGELOG.md
@@ -1,6 +1,33 @@
 ArduinoJson: change log
 =======================
 
+v6.21.2 (2023-04-12)
+-------
+
+* Fix compatibility with the Zephyr Project (issue #1905)
+* Allow using PROGMEM outside of Arduino (issue #1903)
+* Set default for `ARDUINOJSON_ENABLE_PROGMEM` to `1` on AVR
+
+v6.21.1 (2023-03-27)
+-------
+
+* Double speed of `DynamicJsonDocument::garbageCollect()`
+* Fix compatibility with GCC 5.2 (issue #1897)
+
+v6.21.0 (2023-03-14)
+-------
+
+* Drop support for C++98/C++03. Minimum required is C++11.
+* Remove `ARDUINOJSON_NAMESPACE`; use `ArduinoJson` instead.
+* Make string support generic (issue #1807)
+
+v6.20.1 (2023-02-08)
+-------
+
+* Remove explicit exclusion of `as<char*>()` and `as<char>()` (issue #1860)
+  If you try to call them, you'll now get the same error message as any unsupported type.
+  You could also add a custom converter for `char*` and `char`.
+
 v6.20.0 (2022-12-26)
 -------
 
diff --git a/neosensor/libraries/ArduinoJson/CMakeLists.txt b/neosensor/libraries/ArduinoJson/CMakeLists.txt
index 7841c0ec..ca0c6479 100644
--- a/neosensor/libraries/ArduinoJson/CMakeLists.txt
+++ b/neosensor/libraries/ArduinoJson/CMakeLists.txt
@@ -1,19 +1,19 @@
 # ArduinoJson - https://arduinojson.org
-# Copyright © 2014-2022, Benoit BLANCHON
+# Copyright © 2014-2023, Benoit BLANCHON
 # MIT License
 
 cmake_minimum_required(VERSION 3.15)
 
 if(ESP_PLATFORM)
-   # Build ArduinoJson as an ESP-IDF component
-   idf_component_register(INCLUDE_DIRS src)
-   return()
+	# Build ArduinoJson as an ESP-IDF component
+	idf_component_register(INCLUDE_DIRS src)
+	return()
 endif()
 
-project(ArduinoJson VERSION 6.20.0)
+project(ArduinoJson VERSION 6.21.2)
 
 if(CMAKE_PROJECT_NAME STREQUAL PROJECT_NAME)
-    include(CTest)
+	include(CTest)
 endif()
 
 add_subdirectory(src)
diff --git a/neosensor/libraries/ArduinoJson/LICENSE.md b/neosensor/libraries/ArduinoJson/LICENSE.txt
similarity index 95%
rename from neosensor/libraries/ArduinoJson/LICENSE.md
rename to neosensor/libraries/ArduinoJson/LICENSE.txt
index dee0b5ba..15f1b9fb 100644
--- a/neosensor/libraries/ArduinoJson/LICENSE.md
+++ b/neosensor/libraries/ArduinoJson/LICENSE.txt
@@ -1,10 +1,10 @@
-The MIT License (MIT)
----------------------
-
-Copyright © 2014-2022, Benoit BLANCHON
-
-Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the “Software”), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
-
-The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
-
-THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+The MIT License (MIT)
+---------------------
+
+Copyright © 2014-2023, Benoit BLANCHON
+
+Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the “Software”), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
diff --git a/neosensor/libraries/ArduinoJson/README.md b/neosensor/libraries/ArduinoJson/README.md
index 1e843d80..dbd78a8a 100644
--- a/neosensor/libraries/ArduinoJson/README.md
+++ b/neosensor/libraries/ArduinoJson/README.md
@@ -1,17 +1,18 @@
 <p align="center">
-  <a href="https://arduinojson.org/"><img alt="ArduinoJson" src="logo.svg" /></a>
+  <a href="https://arduinojson.org/"><img alt="ArduinoJson" src="https://arduinojson.org/images/logo.svg" width="200" /></a>
 </p>
 
 ---
 
-[![GitHub Workflow Status](https://img.shields.io/github/actions/workflow/status/bblanchon/ArduinoJson/ci.yml?branch=6.x)](https://github.com/bblanchon/ArduinoJson/actions?query=workflow%3A%22Continuous+Integration%22+branch%3A6.x)
+[![GitHub Workflow Status](https://img.shields.io/github/actions/workflow/status/bblanchon/ArduinoJson/ci.yml?branch=6.x&logo=github)](https://github.com/bblanchon/ArduinoJson/actions?query=workflow%3A%22Continuous+Integration%22+branch%3A6.x)
 [![Continuous Integration](https://ci.appveyor.com/api/projects/status/m7s53wav1l0abssg/branch/6.x?svg=true)](https://ci.appveyor.com/project/bblanchon/arduinojson/branch/6.x)
 [![Fuzzing Status](https://oss-fuzz-build-logs.storage.googleapis.com/badges/arduinojson.svg)](https://bugs.chromium.org/p/oss-fuzz/issues/list?sort=-opened&can=1&q=proj:arduinojson)
 [![Coveralls branch](https://img.shields.io/coveralls/github/bblanchon/ArduinoJson/6.x?logo=coveralls)](https://coveralls.io/github/bblanchon/ArduinoJson?branch=6.x)  
-[![Arduino Library Manager](https://img.shields.io/static/v1?label=Arduino&message=v6.20.0&logo=arduino&logoColor=white&color=blue)](https://www.ardu-badge.com/ArduinoJson/6.20.0)
-[![PlatformIO Registry](https://badges.registry.platformio.org/packages/bblanchon/library/ArduinoJson.svg?version=6.20.0)](https://registry.platformio.org/packages/libraries/bblanchon/ArduinoJson?version=6.20.0) 
-[![GitHub stars](https://img.shields.io/github/stars/bblanchon/ArduinoJson?style=flat&logo=github)](https://github.com/bblanchon/ArduinoJson/stargazers)
-[![GitHub Sponsors](https://img.shields.io/github/sponsors/bblanchon?logo=github)](https://github.com/sponsors/bblanchon)
+[![Arduino Library Manager](https://img.shields.io/static/v1?label=Arduino&message=v6.21.2&logo=arduino&logoColor=white&color=blue)](https://www.ardu-badge.com/ArduinoJson/6.21.2)
+[![PlatformIO Registry](https://badges.registry.platformio.org/packages/bblanchon/library/ArduinoJson.svg?version=6.21.2)](https://registry.platformio.org/packages/libraries/bblanchon/ArduinoJson?version=6.21.2) 
+[![ESP IDF](https://img.shields.io/static/v1?label=ESP+IDF&message=v6.21.2&logo=cpu&logoColor=white&color=blue)](https://components.espressif.com/components/bblanchon/arduinojson)  
+[![GitHub stars](https://img.shields.io/github/stars/bblanchon/ArduinoJson?style=flat&logo=github&color=orange)](https://github.com/bblanchon/ArduinoJson/stargazers)
+[![GitHub Sponsors](https://img.shields.io/github/sponsors/bblanchon?logo=github&color=orange)](https://github.com/sponsors/bblanchon)
 
 ArduinoJson is a C++ JSON library for Arduino and IoT (Internet Of Things).
 
@@ -45,7 +46,8 @@ ArduinoJson is a C++ JSON library for Arduino and IoT (Internet Of Things).
     * Supports [custom converters](https://arduinojson.org/news/2021/05/04/version-6-18-0/)
 * Portable
     * Usable on any C++ project (not limited to Arduino)
-    * Compatible with C++98, C++11, C++14 and C++17
+    * Compatible with C++11, C++14 and C++17
+    * Support for C++98/C++03 available on [ArduinoJson 6.20.x](https://github.com/bblanchon/ArduinoJson/tree/6.20.x)
     * Zero warnings with `-Wall -Wextra -pedantic` and `/W4`
     * [Header-only library](https://en.wikipedia.org/wiki/Header-only)
     * Works with virtually any board
@@ -82,9 +84,9 @@ ArduinoJson is a C++ JSON library for Arduino and IoT (Internet Of Things).
 * Well tested
     * [Unit test coverage close to 100%](https://coveralls.io/github/bblanchon/ArduinoJson?branch=6.x)
     * Continuously tested on
-        * [Visual Studio 2010, 2012, 2013, 2015, 2017, 2019, 2022](https://ci.appveyor.com/project/bblanchon/arduinojson/branch/6.x)
-        * [GCC 4.4, 4.6, 4.7, 4.8, 4.9, 5, 6, 7, 8, 9, 10, 11](https://github.com/bblanchon/ArduinoJson/actions?query=workflow%3A%22Continuous+Integration%22)
-        * [Clang 3.5, 3.6, 3.7, 3.8, 3.9, 4.0, 5.0, 6.0, 7, 8, 9, 10](https://github.com/bblanchon/ArduinoJson/actions?query=workflow%3A%22Continuous+Integration%22)
+        * [Visual Studio 2017, 2019, 2022](https://ci.appveyor.com/project/bblanchon/arduinojson/branch/6.x)
+        * [GCC 5, 6, 7, 8, 9, 10, 11](https://github.com/bblanchon/ArduinoJson/actions?query=workflow%3A%22Continuous+Integration%22)
+        * [Clang 3.8, 3.9, 4.0, 5.0, 6.0, 7, 8, 9, 10](https://github.com/bblanchon/ArduinoJson/actions?query=workflow%3A%22Continuous+Integration%22)
     * [Continuously fuzzed with Google OSS Fuzz](https://bugs.chromium.org/p/oss-fuzz/issues/list?sort=-opened&can=1&q=proj:arduinojson)
     * Passes all default checks of [clang-tidy](https://releases.llvm.org/10.0.0/tools/clang/tools/extra/docs/clang-tidy/)
 * Well documented
@@ -99,7 +101,6 @@ ArduinoJson is a C++ JSON library for Arduino and IoT (Internet Of Things).
     * Most popular of all Arduino libraries on [GitHub](https://github.com/search?o=desc&q=arduino+library&s=stars&type=Repositories)
     * [Used in hundreds of projects](https://www.hackster.io/search?i=projects&q=arduinojson)
     * [Responsive support](https://github.com/bblanchon/ArduinoJson/issues?q=is%3Aissue+is%3Aclosed)
-    * [Discord server](https://discord.gg/DzN6hHHD4h)
 
 ## Quickstart
 
diff --git a/neosensor/libraries/ArduinoJson/appveyor.yml b/neosensor/libraries/ArduinoJson/appveyor.yml
index 0276d5d1..7948c646 100644
--- a/neosensor/libraries/ArduinoJson/appveyor.yml
+++ b/neosensor/libraries/ArduinoJson/appveyor.yml
@@ -1,4 +1,4 @@
-version: 6.20.0.{build}
+version: 6.21.2.{build}
 environment:
   matrix:
     - APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2022
@@ -7,15 +7,6 @@ environment:
       CMAKE_GENERATOR: Visual Studio 16 2019
     - APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2017
       CMAKE_GENERATOR: Visual Studio 15 2017
-    - APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2015
-      CMAKE_GENERATOR: Visual Studio 14 2015
-    - CMAKE_GENERATOR: Visual Studio 12 2013
-    - CMAKE_GENERATOR: Visual Studio 11 2012
-    - CMAKE_GENERATOR: Visual Studio 10 2010
-    - CMAKE_GENERATOR: Ninja
-      MINGW: MinGW # MinGW 32-bit 5.3.0
-    - CMAKE_GENERATOR: Ninja
-      MINGW32: i686-5.3.0-posix-dwarf-rt_v4-rev0 # MinGW-w64 5.3.0
     - CMAKE_GENERATOR: Ninja
       MINGW32: i686-6.3.0-posix-dwarf-rt_v5-rev1 # MinGW-w64 6.3.0 i686
     - CMAKE_GENERATOR: Ninja
diff --git a/neosensor/libraries/ArduinoJson/examples/JsonConfigFile/JsonConfigFile.ino b/neosensor/libraries/ArduinoJson/examples/JsonConfigFile/JsonConfigFile.ino
index 3443ba72..b2819d55 100644
--- a/neosensor/libraries/ArduinoJson/examples/JsonConfigFile/JsonConfigFile.ino
+++ b/neosensor/libraries/ArduinoJson/examples/JsonConfigFile/JsonConfigFile.ino
@@ -1,5 +1,5 @@
 // ArduinoJson - https://arduinojson.org
-// Copyright © 2014-2022, Benoit BLANCHON
+// Copyright © 2014-2023, Benoit BLANCHON
 // MIT License
 //
 // This example shows how to store your project configuration in a file.
diff --git a/neosensor/libraries/ArduinoJson/examples/JsonFilterExample/JsonFilterExample.ino b/neosensor/libraries/ArduinoJson/examples/JsonFilterExample/JsonFilterExample.ino
index b546cd1d..fc980aab 100644
--- a/neosensor/libraries/ArduinoJson/examples/JsonFilterExample/JsonFilterExample.ino
+++ b/neosensor/libraries/ArduinoJson/examples/JsonFilterExample/JsonFilterExample.ino
@@ -1,5 +1,5 @@
 // ArduinoJson - https://arduinojson.org
-// Copyright © 2014-2022, Benoit BLANCHON
+// Copyright © 2014-2023, Benoit BLANCHON
 // MIT License
 //
 // This example shows how to use DeserializationOption::Filter
diff --git a/neosensor/libraries/ArduinoJson/examples/JsonGeneratorExample/JsonGeneratorExample.ino b/neosensor/libraries/ArduinoJson/examples/JsonGeneratorExample/JsonGeneratorExample.ino
index ae1c0e25..cf4ab0d9 100644
--- a/neosensor/libraries/ArduinoJson/examples/JsonGeneratorExample/JsonGeneratorExample.ino
+++ b/neosensor/libraries/ArduinoJson/examples/JsonGeneratorExample/JsonGeneratorExample.ino
@@ -1,5 +1,5 @@
 // ArduinoJson - https://arduinojson.org
-// Copyright © 2014-2022, Benoit BLANCHON
+// Copyright © 2014-2023, Benoit BLANCHON
 // MIT License
 //
 // This example shows how to generate a JSON document with ArduinoJson.
diff --git a/neosensor/libraries/ArduinoJson/examples/JsonHttpClient/JsonHttpClient.ino b/neosensor/libraries/ArduinoJson/examples/JsonHttpClient/JsonHttpClient.ino
index 8e2e6d7b..72f311fe 100644
--- a/neosensor/libraries/ArduinoJson/examples/JsonHttpClient/JsonHttpClient.ino
+++ b/neosensor/libraries/ArduinoJson/examples/JsonHttpClient/JsonHttpClient.ino
@@ -1,5 +1,5 @@
 // ArduinoJson - https://arduinojson.org
-// Copyright © 2014-2022, Benoit BLANCHON
+// Copyright © 2014-2023, Benoit BLANCHON
 // MIT License
 //
 // This example shows how to parse a JSON document in an HTTP response.
diff --git a/neosensor/libraries/ArduinoJson/examples/JsonParserExample/JsonParserExample.ino b/neosensor/libraries/ArduinoJson/examples/JsonParserExample/JsonParserExample.ino
index 10f14199..a9d8b6d7 100644
--- a/neosensor/libraries/ArduinoJson/examples/JsonParserExample/JsonParserExample.ino
+++ b/neosensor/libraries/ArduinoJson/examples/JsonParserExample/JsonParserExample.ino
@@ -1,5 +1,5 @@
 // ArduinoJson - https://arduinojson.org
-// Copyright © 2014-2022, Benoit BLANCHON
+// Copyright © 2014-2023, Benoit BLANCHON
 // MIT License
 //
 // This example shows how to deserialize a JSON document with ArduinoJson.
diff --git a/neosensor/libraries/ArduinoJson/examples/JsonServer/JsonServer.ino b/neosensor/libraries/ArduinoJson/examples/JsonServer/JsonServer.ino
index 52d55c5b..a897fce6 100644
--- a/neosensor/libraries/ArduinoJson/examples/JsonServer/JsonServer.ino
+++ b/neosensor/libraries/ArduinoJson/examples/JsonServer/JsonServer.ino
@@ -1,5 +1,5 @@
 // ArduinoJson - https://arduinojson.org
-// Copyright © 2014-2022, Benoit BLANCHON
+// Copyright © 2014-2023, Benoit BLANCHON
 // MIT License
 //
 // This example shows how to implement an HTTP server that sends a JSON document
diff --git a/neosensor/libraries/ArduinoJson/examples/JsonUdpBeacon/JsonUdpBeacon.ino b/neosensor/libraries/ArduinoJson/examples/JsonUdpBeacon/JsonUdpBeacon.ino
index 87965a3c..6c369faa 100644
--- a/neosensor/libraries/ArduinoJson/examples/JsonUdpBeacon/JsonUdpBeacon.ino
+++ b/neosensor/libraries/ArduinoJson/examples/JsonUdpBeacon/JsonUdpBeacon.ino
@@ -1,5 +1,5 @@
 // ArduinoJson - https://arduinojson.org
-// Copyright © 2014-2022, Benoit BLANCHON
+// Copyright © 2014-2023, Benoit BLANCHON
 // MIT License
 //
 // This example shows how to send a JSON document to a UDP socket.
diff --git a/neosensor/libraries/ArduinoJson/examples/MsgPackParser/MsgPackParser.ino b/neosensor/libraries/ArduinoJson/examples/MsgPackParser/MsgPackParser.ino
index a14245b5..1a54c3b3 100644
--- a/neosensor/libraries/ArduinoJson/examples/MsgPackParser/MsgPackParser.ino
+++ b/neosensor/libraries/ArduinoJson/examples/MsgPackParser/MsgPackParser.ino
@@ -1,5 +1,5 @@
 // ArduinoJson - https://arduinojson.org
-// Copyright © 2014-2022, Benoit BLANCHON
+// Copyright © 2014-2023, Benoit BLANCHON
 // MIT License
 //
 // This example shows how to deserialize a MessagePack document with
diff --git a/neosensor/libraries/ArduinoJson/examples/ProgmemExample/ProgmemExample.ino b/neosensor/libraries/ArduinoJson/examples/ProgmemExample/ProgmemExample.ino
index 1a13ea00..68b8ec56 100644
--- a/neosensor/libraries/ArduinoJson/examples/ProgmemExample/ProgmemExample.ino
+++ b/neosensor/libraries/ArduinoJson/examples/ProgmemExample/ProgmemExample.ino
@@ -1,5 +1,5 @@
 // ArduinoJson - https://arduinojson.org
-// Copyright © 2014-2022, Benoit BLANCHON
+// Copyright © 2014-2023, Benoit BLANCHON
 // MIT License
 //
 // This example shows the different ways you can use Flash strings with
diff --git a/neosensor/libraries/ArduinoJson/examples/StringExample/StringExample.ino b/neosensor/libraries/ArduinoJson/examples/StringExample/StringExample.ino
index 3d8f9e4a..3e51b915 100644
--- a/neosensor/libraries/ArduinoJson/examples/StringExample/StringExample.ino
+++ b/neosensor/libraries/ArduinoJson/examples/StringExample/StringExample.ino
@@ -1,5 +1,5 @@
 // ArduinoJson - https://arduinojson.org
-// Copyright © 2014-2022, Benoit BLANCHON
+// Copyright © 2014-2023, Benoit BLANCHON
 // MIT License
 //
 // This example shows the different ways you can use String with ArduinoJson.
diff --git a/neosensor/libraries/ArduinoJson/extras/CompileOptions.cmake b/neosensor/libraries/ArduinoJson/extras/CompileOptions.cmake
index 82a750a1..0cbd576c 100644
--- a/neosensor/libraries/ArduinoJson/extras/CompileOptions.cmake
+++ b/neosensor/libraries/ArduinoJson/extras/CompileOptions.cmake
@@ -27,11 +27,10 @@ if(CMAKE_CXX_COMPILER_ID MATCHES "(GNU|Clang)")
 	if(${COVERAGE})
 		set(CMAKE_CXX_FLAGS "-fprofile-arcs -ftest-coverage")
 	endif()
-
 endif()
 
-if(CMAKE_CXX_COMPILER_ID STREQUAL  "GNU")
-	if((CMAKE_CXX_COMPILER_VERSION VERSION_GREATER 4.8) AND (NOT ${COVERAGE}))
+if(CMAKE_CXX_COMPILER_ID STREQUAL "GNU")
+	if((CMAKE_CXX_COMPILER_VERSION VERSION_GREATER 4.8) AND(NOT ${COVERAGE}))
 		add_compile_options(-g -Og)
 	else()
 		add_compile_options(-g -O0)
@@ -65,7 +64,7 @@ if(CMAKE_CXX_COMPILER_ID MATCHES "Clang")
 endif()
 
 if(CMAKE_CXX_COMPILER_ID STREQUAL "Clang")
-	if((CMAKE_CXX_COMPILER_VERSION VERSION_GREATER 4.0) AND (NOT ${COVERAGE}))
+	if((CMAKE_CXX_COMPILER_VERSION VERSION_GREATER 4.0) AND(NOT ${COVERAGE}))
 		add_compile_options(-g -Og)
 	else()
 		add_compile_options(-g -O0)
@@ -73,7 +72,7 @@ if(CMAKE_CXX_COMPILER_ID STREQUAL "Clang")
 endif()
 
 if(CMAKE_CXX_COMPILER_ID STREQUAL "AppleClang")
-	if((CMAKE_CXX_COMPILER_VERSION VERSION_GREATER 9.0) AND (NOT ${COVERAGE}))
+	if((CMAKE_CXX_COMPILER_VERSION VERSION_GREATER 9.0) AND(NOT ${COVERAGE}))
 		add_compile_options(-g -Og)
 	else()
 		add_compile_options(-g -O0)
@@ -85,17 +84,12 @@ if(MSVC)
 	add_compile_options(
 		/W4 # Set warning level
 		/WX # Treats all compiler warnings as errors.
+		/Zc:__cplusplus # Enable updated __cplusplus macro
 	)
-
-	if (NOT MSVC_VERSION LESS  1910) #  >= Visual Studio 2017
-		add_compile_options(
-			/Zc:__cplusplus  # Enable updated __cplusplus macro
-		)
-	endif()
 endif()
 
 if(MINGW)
-  # Static link on MinGW to avoid linking with the wrong DLLs when multiple
+	# Static link on MinGW to avoid linking with the wrong DLLs when multiple
 	# versions are installed.
 	add_link_options(-static)
 endif()
diff --git a/neosensor/libraries/ArduinoJson/extras/ci/espidf/CMakeLists.txt b/neosensor/libraries/ArduinoJson/extras/ci/espidf/CMakeLists.txt
index e3c4475a..3b878969 100644
--- a/neosensor/libraries/ArduinoJson/extras/ci/espidf/CMakeLists.txt
+++ b/neosensor/libraries/ArduinoJson/extras/ci/espidf/CMakeLists.txt
@@ -1,5 +1,5 @@
 # ArduinoJson - https://arduinojson.org
-# Copyright © 2014-2022, Benoit BLANCHON
+# Copyright © 2014-2023, Benoit BLANCHON
 # MIT License
 
 cmake_minimum_required(VERSION 3.5)
diff --git a/neosensor/libraries/ArduinoJson/extras/ci/espidf/main/CMakeLists.txt b/neosensor/libraries/ArduinoJson/extras/ci/espidf/main/CMakeLists.txt
index f06bfda6..3e10ef8f 100644
--- a/neosensor/libraries/ArduinoJson/extras/ci/espidf/main/CMakeLists.txt
+++ b/neosensor/libraries/ArduinoJson/extras/ci/espidf/main/CMakeLists.txt
@@ -1,6 +1,8 @@
 # ArduinoJson - https://arduinojson.org
-# Copyright © 2014-2022, Benoit BLANCHON
+# Copyright © 2014-2023, Benoit BLANCHON
 # MIT License
 
-idf_component_register(SRCS "main.cpp"
-                       INCLUDE_DIRS "")
+idf_component_register(
+	SRCS "main.cpp"
+	INCLUDE_DIRS ""
+)
diff --git a/neosensor/libraries/ArduinoJson/extras/ci/espidf/main/main.cpp b/neosensor/libraries/ArduinoJson/extras/ci/espidf/main/main.cpp
index 50d7ec07..4818a679 100644
--- a/neosensor/libraries/ArduinoJson/extras/ci/espidf/main/main.cpp
+++ b/neosensor/libraries/ArduinoJson/extras/ci/espidf/main/main.cpp
@@ -1,5 +1,5 @@
 // ArduinoJson - https://arduinojson.org
-// Copyright © 2014-2022, Benoit BLANCHON
+// Copyright © 2014-2023, Benoit BLANCHON
 // MIT License
 
 #include <ArduinoJson.h>
diff --git a/neosensor/libraries/ArduinoJson/extras/conf_test/avr.cpp b/neosensor/libraries/ArduinoJson/extras/conf_test/avr.cpp
index cd0b4bdc..b2b105e3 100644
--- a/neosensor/libraries/ArduinoJson/extras/conf_test/avr.cpp
+++ b/neosensor/libraries/ArduinoJson/extras/conf_test/avr.cpp
@@ -1,5 +1,7 @@
 #include <ArduinoJson.h>
 
+static_assert(ARDUINOJSON_ENABLE_PROGMEM == 1, "ARDUINOJSON_ENABLE_PROGMEM");
+
 static_assert(ARDUINOJSON_USE_LONG_LONG == 0, "ARDUINOJSON_USE_LONG_LONG");
 
 static_assert(ARDUINOJSON_SLOT_OFFSET_SIZE == 1,
@@ -9,7 +11,7 @@ static_assert(ARDUINOJSON_LITTLE_ENDIAN == 1, "ARDUINOJSON_LITTLE_ENDIAN");
 
 static_assert(ARDUINOJSON_USE_DOUBLE == 1, "ARDUINOJSON_USE_DOUBLE");
 
-static_assert(sizeof(ARDUINOJSON_NAMESPACE::VariantSlot) == 8,
+static_assert(sizeof(ArduinoJson::detail::VariantSlot) == 8,
               "sizeof(VariantSlot)");
 
 void setup() {}
diff --git a/neosensor/libraries/ArduinoJson/extras/conf_test/esp8266.cpp b/neosensor/libraries/ArduinoJson/extras/conf_test/esp8266.cpp
index bb9988bd..caf70b2a 100644
--- a/neosensor/libraries/ArduinoJson/extras/conf_test/esp8266.cpp
+++ b/neosensor/libraries/ArduinoJson/extras/conf_test/esp8266.cpp
@@ -9,7 +9,7 @@ static_assert(ARDUINOJSON_LITTLE_ENDIAN == 1, "ARDUINOJSON_LITTLE_ENDIAN");
 
 static_assert(ARDUINOJSON_USE_DOUBLE == 1, "ARDUINOJSON_USE_DOUBLE");
 
-static_assert(sizeof(ARDUINOJSON_NAMESPACE::VariantSlot) == 16,
+static_assert(sizeof(ArduinoJson::detail::VariantSlot) == 16,
               "sizeof(VariantSlot)");
 
 void setup() {}
diff --git a/neosensor/libraries/ArduinoJson/extras/conf_test/x64.cpp b/neosensor/libraries/ArduinoJson/extras/conf_test/x64.cpp
index 8cd6c06e..97838e34 100644
--- a/neosensor/libraries/ArduinoJson/extras/conf_test/x64.cpp
+++ b/neosensor/libraries/ArduinoJson/extras/conf_test/x64.cpp
@@ -9,7 +9,7 @@ static_assert(ARDUINOJSON_LITTLE_ENDIAN == 1, "ARDUINOJSON_LITTLE_ENDIAN");
 
 static_assert(ARDUINOJSON_USE_DOUBLE == 1, "ARDUINOJSON_USE_DOUBLE");
 
-static_assert(sizeof(ARDUINOJSON_NAMESPACE::VariantSlot) == 32,
+static_assert(sizeof(ArduinoJson::detail::VariantSlot) == 32,
               "sizeof(VariantSlot)");
 
 int main() {}
diff --git a/neosensor/libraries/ArduinoJson/extras/conf_test/x86.cpp b/neosensor/libraries/ArduinoJson/extras/conf_test/x86.cpp
index dab9da61..f3dab30f 100644
--- a/neosensor/libraries/ArduinoJson/extras/conf_test/x86.cpp
+++ b/neosensor/libraries/ArduinoJson/extras/conf_test/x86.cpp
@@ -9,7 +9,7 @@ static_assert(ARDUINOJSON_LITTLE_ENDIAN == 1, "ARDUINOJSON_LITTLE_ENDIAN");
 
 static_assert(ARDUINOJSON_USE_DOUBLE == 1, "ARDUINOJSON_USE_DOUBLE");
 
-static_assert(sizeof(ARDUINOJSON_NAMESPACE::VariantSlot) == 16,
+static_assert(sizeof(ArduinoJson::detail::VariantSlot) == 16,
               "sizeof(VariantSlot)");
 
 int main() {}
diff --git a/neosensor/libraries/ArduinoJson/extras/fuzzing/CMakeLists.txt b/neosensor/libraries/ArduinoJson/extras/fuzzing/CMakeLists.txt
index e5d6a36e..1d439f46 100644
--- a/neosensor/libraries/ArduinoJson/extras/fuzzing/CMakeLists.txt
+++ b/neosensor/libraries/ArduinoJson/extras/fuzzing/CMakeLists.txt
@@ -1,7 +1,10 @@
 # ArduinoJson - https://arduinojson.org
-# Copyright © 2014-2022, Benoit BLANCHON
+# Copyright © 2014-2023, Benoit BLANCHON
 # MIT License
 
+set(CMAKE_CXX_STANDARD 11)
+set(CMAKE_CXX_STANDARD_REQUIRED ON)
+
 if(MSVC)
 	add_compile_options(-D_CRT_SECURE_NO_WARNINGS)
 endif()
@@ -22,7 +25,7 @@ target_link_libraries(json_reproducer
 	ArduinoJson
 )
 
-macro(add_fuzzer name)	
+macro(add_fuzzer name)
 	set(FUZZER "${name}_fuzzer")
 	set(CORPUS_DIR "${CMAKE_CURRENT_SOURCE_DIR}/${name}_corpus")
 	set(SEED_CORPUS_DIR "${CMAKE_CURRENT_SOURCE_DIR}/${name}_seed_corpus")
@@ -33,27 +36,23 @@ macro(add_fuzzer name)
 		ArduinoJson
 	)
 	set_target_properties("${FUZZER}"
-		PROPERTIES 
-			COMPILE_FLAGS  
-				"-fprofile-instr-generate -fcoverage-mapping -fsanitize=fuzzer -fno-sanitize-recover=all"
-			LINK_FLAGS
-				"-fprofile-instr-generate -fcoverage-mapping -fsanitize=fuzzer -fno-sanitize-recover=all"
+		PROPERTIES
+			COMPILE_FLAGS "-fprofile-instr-generate -fcoverage-mapping -fsanitize=fuzzer -fno-sanitize-recover=all"
+			LINK_FLAGS "-fprofile-instr-generate -fcoverage-mapping -fsanitize=fuzzer -fno-sanitize-recover=all"
 	)
 
 	add_test(
-		NAME
-			"${FUZZER}"
-		COMMAND
-			"${FUZZER}" "${CORPUS_DIR}" "${SEED_CORPUS_DIR}" -max_total_time=5 -timeout=1
+		NAME "${FUZZER}"
+		COMMAND "${FUZZER}" "${CORPUS_DIR}" "${SEED_CORPUS_DIR}" -max_total_time=5 -timeout=1
 	)
 
 	set_tests_properties("${FUZZER}"
 		PROPERTIES
-			LABELS 		"Fuzzing"
+		LABELS "Fuzzing"
 	)
 endmacro()
 
-if (CMAKE_CXX_COMPILER_ID STREQUAL "Clang" AND CMAKE_CXX_COMPILER_VERSION VERSION_GREATER 6)
+if(CMAKE_CXX_COMPILER_ID STREQUAL "Clang" AND CMAKE_CXX_COMPILER_VERSION VERSION_GREATER 6)
 	add_fuzzer(json)
 	add_fuzzer(msgpack)
 endif()
diff --git a/neosensor/libraries/ArduinoJson/extras/fuzzing/Makefile b/neosensor/libraries/ArduinoJson/extras/fuzzing/Makefile
index d3c574e1..81fc4125 100644
--- a/neosensor/libraries/ArduinoJson/extras/fuzzing/Makefile
+++ b/neosensor/libraries/ArduinoJson/extras/fuzzing/Makefile
@@ -1,6 +1,6 @@
 # CAUTION: this file is invoked by https://github.com/google/oss-fuzz
 
-CXXFLAGS += -I../../src -DARDUINOJSON_DEBUG=1
+CXXFLAGS += -I../../src -DARDUINOJSON_DEBUG=1 -std=c++11
 
 all: \
 	$(OUT)/json_fuzzer \
diff --git a/neosensor/libraries/ArduinoJson/extras/fuzzing/reproducer.cpp b/neosensor/libraries/ArduinoJson/extras/fuzzing/reproducer.cpp
index 86818012..a004d01e 100644
--- a/neosensor/libraries/ArduinoJson/extras/fuzzing/reproducer.cpp
+++ b/neosensor/libraries/ArduinoJson/extras/fuzzing/reproducer.cpp
@@ -1,5 +1,5 @@
 // ArduinoJson - https://arduinojson.org
-// Copyright © 2014-2022, Benoit BLANCHON
+// Copyright © 2014-2023, Benoit BLANCHON
 // MIT License
 
 // This file is NOT use by Google's OSS fuzz
diff --git a/neosensor/libraries/ArduinoJson/extras/scripts/build-arduino-package.sh b/neosensor/libraries/ArduinoJson/extras/scripts/build-arduino-package.sh
index 339db2f6..09f6efb3 100644
--- a/neosensor/libraries/ArduinoJson/extras/scripts/build-arduino-package.sh
+++ b/neosensor/libraries/ArduinoJson/extras/scripts/build-arduino-package.sh
@@ -18,6 +18,6 @@ rm -f "$OUTPUT"
 	src \
 	keywords.txt \
 	library.properties \
-	LICENSE.md \
+	LICENSE.txt \
 	README.md \
 	ArduinoJson.h
diff --git a/neosensor/libraries/ArduinoJson/extras/scripts/build-single-header.sh b/neosensor/libraries/ArduinoJson/extras/scripts/build-single-header.sh
index 09d91d41..00292e30 100644
--- a/neosensor/libraries/ArduinoJson/extras/scripts/build-single-header.sh
+++ b/neosensor/libraries/ArduinoJson/extras/scripts/build-single-header.sh
@@ -53,7 +53,8 @@ process()
 }
 
 simplify_namespaces() {
-	perl -p0i -e 's|\}  // namespace ARDUINOJSON_NAMESPACE\r?\nnamespace ARDUINOJSON_NAMESPACE \{\r?\n||igs' "$1"
+	perl -p0i -e 's|ARDUINOJSON_END_PUBLIC_NAMESPACE\r?\nARDUINOJSON_BEGIN_PUBLIC_NAMESPACE\r?\n||igs' "$1"
+	perl -p0i -e 's|ARDUINOJSON_END_PRIVATE_NAMESPACE\r?\nARDUINOJSON_BEGIN_PRIVATE_NAMESPACE\r?\n||igs' "$1"
 	rm -f "$1.bak"
 }
 
diff --git a/neosensor/libraries/ArduinoJson/extras/scripts/publish-particle-library.sh b/neosensor/libraries/ArduinoJson/extras/scripts/publish-particle-library.sh
index 62c3140f..3ee7f912 100644
--- a/neosensor/libraries/ArduinoJson/extras/scripts/publish-particle-library.sh
+++ b/neosensor/libraries/ArduinoJson/extras/scripts/publish-particle-library.sh
@@ -9,10 +9,10 @@ trap 'rm -rf "$WORK_DIR"' EXIT
 cp "$SOURCE_DIR/README.md" "$WORK_DIR/README.md"
 cp "$SOURCE_DIR/CHANGELOG.md" "$WORK_DIR/CHANGELOG.md"
 cp "$SOURCE_DIR/library.properties" "$WORK_DIR/library.properties"
-cp "$SOURCE_DIR/LICENSE.md" "$WORK_DIR/LICENSE.txt"
+cp "$SOURCE_DIR/LICENSE.txt" "$WORK_DIR/LICENSE.txt"
 cp -r "$SOURCE_DIR/src" "$WORK_DIR/"
 cp -r "$SOURCE_DIR/examples" "$WORK_DIR/"
 
 cd "$WORK_DIR"
-particle library upload
-particle library publish
+particle library upload -v
+particle library publish -v
diff --git a/neosensor/libraries/ArduinoJson/extras/scripts/publish.sh b/neosensor/libraries/ArduinoJson/extras/scripts/publish.sh
index c5a4eefd..a562a24c 100644
--- a/neosensor/libraries/ArduinoJson/extras/scripts/publish.sh
+++ b/neosensor/libraries/ArduinoJson/extras/scripts/publish.sh
@@ -38,6 +38,9 @@ update_version_in_source () {
 	sed -i~ -bE "s/version: .*$/version: $VERSION.{build}/" appveyor.yml
 	rm appveyor.yml~
 
+	sed -i~ -bE "s/^version: .*$/version: \"$VERSION\"/" idf_component.yml
+	rm idf_component.yml~
+
 	sed -i~ -bE \
 		-e "s/ARDUINOJSON_VERSION .*$/ARDUINOJSON_VERSION \"$VERSION\"/" \
 		-e "s/ARDUINOJSON_VERSION_MAJOR .*$/ARDUINOJSON_VERSION_MAJOR $MAJOR/" \
@@ -48,7 +51,7 @@ update_version_in_source () {
 }
 
 commit_new_version () {
-	git add src/ArduinoJson/version.hpp README.md CHANGELOG.md library.json library.properties appveyor.yml CMakeLists.txt
+	git add src/ArduinoJson/version.hpp README.md CHANGELOG.md library.json library.properties appveyor.yml CMakeLists.txt idf_component.yml
 	git commit -m "Set version to $VERSION"
 }
 
diff --git a/neosensor/libraries/ArduinoJson/extras/scripts/wandbox/JsonGeneratorExample.cpp b/neosensor/libraries/ArduinoJson/extras/scripts/wandbox/JsonGeneratorExample.cpp
index 0090c849..b35d1856 100644
--- a/neosensor/libraries/ArduinoJson/extras/scripts/wandbox/JsonGeneratorExample.cpp
+++ b/neosensor/libraries/ArduinoJson/extras/scripts/wandbox/JsonGeneratorExample.cpp
@@ -1,5 +1,5 @@
 // ArduinoJson - https://arduinojson.org
-// Copyright © 2014-2022, Benoit BLANCHON
+// Copyright © 2014-2023, Benoit BLANCHON
 // MIT License
 //
 // This example shows how to generate a JSON document with ArduinoJson.
diff --git a/neosensor/libraries/ArduinoJson/extras/scripts/wandbox/JsonParserExample.cpp b/neosensor/libraries/ArduinoJson/extras/scripts/wandbox/JsonParserExample.cpp
index f513caff..396f98cb 100644
--- a/neosensor/libraries/ArduinoJson/extras/scripts/wandbox/JsonParserExample.cpp
+++ b/neosensor/libraries/ArduinoJson/extras/scripts/wandbox/JsonParserExample.cpp
@@ -1,5 +1,5 @@
 // ArduinoJson - https://arduinojson.org
-// Copyright © 2014-2022, Benoit BLANCHON
+// Copyright © 2014-2023, Benoit BLANCHON
 // MIT License
 //
 // This example shows how to deserialize a JSON document with ArduinoJson.
diff --git a/neosensor/libraries/ArduinoJson/extras/scripts/wandbox/MsgPackParserExample.cpp b/neosensor/libraries/ArduinoJson/extras/scripts/wandbox/MsgPackParserExample.cpp
index 186816e6..763de84e 100644
--- a/neosensor/libraries/ArduinoJson/extras/scripts/wandbox/MsgPackParserExample.cpp
+++ b/neosensor/libraries/ArduinoJson/extras/scripts/wandbox/MsgPackParserExample.cpp
@@ -1,5 +1,5 @@
 // ArduinoJson - https://arduinojson.org
-// Copyright © 2014-2022, Benoit BLANCHON
+// Copyright © 2014-2023, Benoit BLANCHON
 // MIT License
 //
 // This example shows how to generate a JSON document with ArduinoJson.
diff --git a/neosensor/libraries/ArduinoJson/extras/tests/CMakeLists.txt b/neosensor/libraries/ArduinoJson/extras/tests/CMakeLists.txt
index faf4cd9a..c62401f9 100644
--- a/neosensor/libraries/ArduinoJson/extras/tests/CMakeLists.txt
+++ b/neosensor/libraries/ArduinoJson/extras/tests/CMakeLists.txt
@@ -1,8 +1,8 @@
 # ArduinoJson - https://arduinojson.org
-# Copyright © 2014-2022, Benoit BLANCHON
+# Copyright © 2014-2023, Benoit BLANCHON
 # MIT License
 
-set(CMAKE_CXX_STANDARD 98)
+set(CMAKE_CXX_STANDARD 11)
 set(CMAKE_CXX_STANDARD_REQUIRED ON)
 
 add_subdirectory(catch)
@@ -10,7 +10,6 @@ add_subdirectory(catch)
 link_libraries(ArduinoJson catch)
 
 include_directories(Helpers)
-add_subdirectory(Cpp11)
 add_subdirectory(Cpp17)
 add_subdirectory(Cpp20)
 add_subdirectory(FailingBuilds)
diff --git a/neosensor/libraries/ArduinoJson/extras/tests/Cpp11/CMakeLists.txt b/neosensor/libraries/ArduinoJson/extras/tests/Cpp11/CMakeLists.txt
deleted file mode 100644
index 0b84a14c..00000000
--- a/neosensor/libraries/ArduinoJson/extras/tests/Cpp11/CMakeLists.txt
+++ /dev/null
@@ -1,36 +0,0 @@
-# ArduinoJson - https://arduinojson.org
-# Copyright © 2014-2022, Benoit BLANCHON
-# MIT License
-
-if("cxx_nullptr" IN_LIST CMAKE_CXX_COMPILE_FEATURES)
-	list(APPEND SOURCES nullptr.cpp)
-	add_definitions(-DARDUINOJSON_HAS_NULLPTR=1)
-endif()
-
-if("cxx_auto_type" IN_LIST CMAKE_CXX_COMPILE_FEATURES AND "cxx_constexpr" IN_LIST CMAKE_CXX_COMPILE_FEATURES)
-	list(APPEND SOURCES issue1120.cpp)
-endif()
-
-if("cxx_long_long_type" IN_LIST CMAKE_CXX_COMPILE_FEATURES)
-	list(APPEND SOURCES use_long_long_0.cpp use_long_long_1.cpp)
-endif()
-
-if("cxx_range_for" IN_LIST CMAKE_CXX_COMPILE_FEATURES AND "cxx_generalized_initializers" IN_LIST CMAKE_CXX_COMPILE_FEATURES)
-	list(APPEND SOURCES stl_containers.cpp)
-endif()
-
-if(NOT SOURCES)
-	return()
-endif()
-
-set(CMAKE_CXX_STANDARD 11)
-set(CMAKE_CXX_STANDARD_REQUIRED ON)
-
-add_executable(Cpp11Tests ${SOURCES})
-
-add_test(Cpp11 Cpp11Tests)
-
-set_tests_properties(Cpp11
-	PROPERTIES
-		LABELS "Catch"
-)
diff --git a/neosensor/libraries/ArduinoJson/extras/tests/Cpp11/stl_containers.cpp b/neosensor/libraries/ArduinoJson/extras/tests/Cpp11/stl_containers.cpp
deleted file mode 100644
index 9e590c14..00000000
--- a/neosensor/libraries/ArduinoJson/extras/tests/Cpp11/stl_containers.cpp
+++ /dev/null
@@ -1,72 +0,0 @@
-// ArduinoJson - https://arduinojson.org
-// Copyright © 2014-2022, Benoit BLANCHON
-// MIT License
-
-#include <ArduinoJson.h>
-#include <stdint.h>
-#include <catch.hpp>
-
-#include <string>
-#include <vector>
-
-namespace ARDUINOJSON_NAMESPACE {
-template <typename T>
-struct Converter<std::vector<T> > {
-  static void toJson(const std::vector<T>& src, JsonVariant dst) {
-    JsonArray array = dst.to<JsonArray>();
-    for (T item : src)
-      array.add(item);
-  }
-
-  static std::vector<T> fromJson(JsonVariantConst src) {
-    std::vector<T> dst;
-    for (T item : src.as<JsonArrayConst>())
-      dst.push_back(item);
-    return dst;
-  }
-
-  static bool checkJson(JsonVariantConst src) {
-    JsonArrayConst array = src;
-    bool result = array;
-    for (JsonVariantConst item : array) {
-      if (!result)
-        break;
-      result = item.is<T>();
-    }
-    return result;
-  }
-};
-}  // namespace ARDUINOJSON_NAMESPACE
-
-TEST_CASE("vector<int>") {
-  SECTION("toJson") {
-    std::vector<int> v = {1, 2};
-
-    StaticJsonDocument<128> doc;
-    doc.set(v);
-    REQUIRE(doc.as<std::string>() == "[1,2]");
-  }
-
-  SECTION("fromJson") {
-    StaticJsonDocument<128> doc;
-    doc.add(1);
-    doc.add(2);
-
-    auto v = doc.as<std::vector<int> >();
-    REQUIRE(v.size() == 2);
-    CHECK(v[0] == 1);
-    CHECK(v[1] == 2);
-  }
-
-  SECTION("checkJson") {
-    StaticJsonDocument<128> doc;
-    CHECK(doc.is<std::vector<int> >() == false);
-
-    doc.add(1);
-    doc.add(2);
-    CHECK(doc.is<std::vector<int> >() == true);
-
-    doc.add("foo");
-    CHECK(doc.is<std::vector<int> >() == false);
-  }
-}
diff --git a/neosensor/libraries/ArduinoJson/extras/tests/Cpp17/CMakeLists.txt b/neosensor/libraries/ArduinoJson/extras/tests/Cpp17/CMakeLists.txt
index 05af2007..62f2784f 100644
--- a/neosensor/libraries/ArduinoJson/extras/tests/Cpp17/CMakeLists.txt
+++ b/neosensor/libraries/ArduinoJson/extras/tests/Cpp17/CMakeLists.txt
@@ -1,5 +1,5 @@
 # ArduinoJson - https://arduinojson.org
-# Copyright © 2014-2022, Benoit BLANCHON
+# Copyright © 2014-2023, Benoit BLANCHON
 # MIT License
 
 if(MSVC_VERSION LESS 1910)
@@ -25,5 +25,5 @@ add_test(Cpp17 Cpp17Tests)
 
 set_tests_properties(Cpp17
 	PROPERTIES
-		LABELS 		"Catch"
+		LABELS "Catch"
 )
diff --git a/neosensor/libraries/ArduinoJson/extras/tests/Cpp17/string_view.cpp b/neosensor/libraries/ArduinoJson/extras/tests/Cpp17/string_view.cpp
index 729c59ae..6a51ce2d 100644
--- a/neosensor/libraries/ArduinoJson/extras/tests/Cpp17/string_view.cpp
+++ b/neosensor/libraries/ArduinoJson/extras/tests/Cpp17/string_view.cpp
@@ -1,6 +1,6 @@
 #include <ArduinoJson.h>
-
 #include <catch.hpp>
+
 #include <string_view>
 
 #if !ARDUINOJSON_ENABLE_STRING_VIEW
@@ -86,7 +86,7 @@ TEST_CASE("string_view") {
   }
 }
 
-using ARDUINOJSON_NAMESPACE::adaptString;
+using ArduinoJson::detail::adaptString;
 
 TEST_CASE("StringViewAdapter") {
   std::string_view str("bravoXXX", 5);
diff --git a/neosensor/libraries/ArduinoJson/extras/tests/Cpp20/CMakeLists.txt b/neosensor/libraries/ArduinoJson/extras/tests/Cpp20/CMakeLists.txt
index 2c9a72b0..e993e974 100644
--- a/neosensor/libraries/ArduinoJson/extras/tests/Cpp20/CMakeLists.txt
+++ b/neosensor/libraries/ArduinoJson/extras/tests/Cpp20/CMakeLists.txt
@@ -1,5 +1,5 @@
 # ArduinoJson - https://arduinojson.org
-# Copyright © 2014-2022, Benoit BLANCHON
+# Copyright © 2014-2023, Benoit BLANCHON
 # MIT License
 
 if(MSVC_VERSION LESS 1910)
@@ -25,5 +25,5 @@ add_test(Cpp20 Cpp20Tests)
 
 set_tests_properties(Cpp20
 	PROPERTIES
-		LABELS 		"Catch"
+		LABELS "Catch"
 )
diff --git a/neosensor/libraries/ArduinoJson/extras/tests/FailingBuilds/CMakeLists.txt b/neosensor/libraries/ArduinoJson/extras/tests/FailingBuilds/CMakeLists.txt
index dc76c910..0464908c 100644
--- a/neosensor/libraries/ArduinoJson/extras/tests/FailingBuilds/CMakeLists.txt
+++ b/neosensor/libraries/ArduinoJson/extras/tests/FailingBuilds/CMakeLists.txt
@@ -1,29 +1,25 @@
 # ArduinoJson - https://arduinojson.org
-# Copyright © 2014-2022, Benoit BLANCHON
+# Copyright © 2014-2023, Benoit BLANCHON
 # MIT License
 
 macro(build_should_fail target)
 	set_target_properties(${target}
 		PROPERTIES
-	    	EXCLUDE_FROM_ALL TRUE
-	        EXCLUDE_FROM_DEFAULT_BUILD TRUE
+			EXCLUDE_FROM_ALL TRUE
+			EXCLUDE_FROM_DEFAULT_BUILD TRUE
 	)
 	add_test(
-		NAME
-			${target}
-	    COMMAND
-	    	${CMAKE_COMMAND} --build . --target ${target} --config $<CONFIGURATION>
-	    WORKING_DIRECTORY
-	    	${CMAKE_BINARY_DIR}
+		NAME ${target}
+			COMMAND ${CMAKE_COMMAND} --build . --target ${target} --config $<CONFIGURATION>
+			WORKING_DIRECTORY ${CMAKE_BINARY_DIR}
 	)
-	set_tests_properties(${target} 
+	set_tests_properties(${target}
 		PROPERTIES
-			WILL_FAIL	TRUE
-			LABELS 		"WillFail;Catch"
+			WILL_FAIL TRUE
+			LABELS "WillFail;Catch"
 	)
 endmacro()
 
-
 add_executable(Issue978 Issue978.cpp)
 build_should_fail(Issue978)
 
@@ -31,11 +27,9 @@ add_executable(Issue1189 Issue1189.cpp)
 build_should_fail(Issue1189)
 
 add_executable(read_long_long read_long_long.cpp)
-set_property(TARGET read_long_long PROPERTY CXX_STANDARD 11)
 build_should_fail(read_long_long)
 
 add_executable(write_long_long write_long_long.cpp)
-set_property(TARGET write_long_long PROPERTY CXX_STANDARD 11)
 build_should_fail(write_long_long)
 
 add_executable(delete_jsondocument delete_jsondocument.cpp)
diff --git a/neosensor/libraries/ArduinoJson/extras/tests/FailingBuilds/Issue1189.cpp b/neosensor/libraries/ArduinoJson/extras/tests/FailingBuilds/Issue1189.cpp
index e86bb992..40cb59f8 100644
--- a/neosensor/libraries/ArduinoJson/extras/tests/FailingBuilds/Issue1189.cpp
+++ b/neosensor/libraries/ArduinoJson/extras/tests/FailingBuilds/Issue1189.cpp
@@ -1,5 +1,5 @@
 // ArduinoJson - https://arduinojson.org
-// Copyright © 2014-2022, Benoit BLANCHON
+// Copyright © 2014-2023, Benoit BLANCHON
 // MIT License
 
 #include <ArduinoJson.h>
diff --git a/neosensor/libraries/ArduinoJson/extras/tests/FailingBuilds/Issue978.cpp b/neosensor/libraries/ArduinoJson/extras/tests/FailingBuilds/Issue978.cpp
index 112d36d8..d85aaa0c 100644
--- a/neosensor/libraries/ArduinoJson/extras/tests/FailingBuilds/Issue978.cpp
+++ b/neosensor/libraries/ArduinoJson/extras/tests/FailingBuilds/Issue978.cpp
@@ -1,5 +1,5 @@
 // ArduinoJson - https://arduinojson.org
-// Copyright © 2014-2022, Benoit BLANCHON
+// Copyright © 2014-2023, Benoit BLANCHON
 // MIT License
 
 #include <ArduinoJson.h>
diff --git a/neosensor/libraries/ArduinoJson/extras/tests/FailingBuilds/assign_char.cpp b/neosensor/libraries/ArduinoJson/extras/tests/FailingBuilds/assign_char.cpp
index 633b54de..b9d897f7 100644
--- a/neosensor/libraries/ArduinoJson/extras/tests/FailingBuilds/assign_char.cpp
+++ b/neosensor/libraries/ArduinoJson/extras/tests/FailingBuilds/assign_char.cpp
@@ -1,5 +1,5 @@
 // ArduinoJson - https://arduinojson.org
-// Copyright © 2014-2022, Benoit BLANCHON
+// Copyright © 2014-2023, Benoit BLANCHON
 // MIT License
 
 #include <ArduinoJson.h>
diff --git a/neosensor/libraries/ArduinoJson/extras/tests/FailingBuilds/delete_jsondocument.cpp b/neosensor/libraries/ArduinoJson/extras/tests/FailingBuilds/delete_jsondocument.cpp
index ef2b0e86..174b80a5 100644
--- a/neosensor/libraries/ArduinoJson/extras/tests/FailingBuilds/delete_jsondocument.cpp
+++ b/neosensor/libraries/ArduinoJson/extras/tests/FailingBuilds/delete_jsondocument.cpp
@@ -1,5 +1,5 @@
 // ArduinoJson - https://arduinojson.org
-// Copyright © 2014-2022, Benoit BLANCHON
+// Copyright © 2014-2023, Benoit BLANCHON
 // MIT License
 
 #include <ArduinoJson.h>
diff --git a/neosensor/libraries/ArduinoJson/extras/tests/FailingBuilds/read_long_long.cpp b/neosensor/libraries/ArduinoJson/extras/tests/FailingBuilds/read_long_long.cpp
index d900a7f3..6fe5992a 100644
--- a/neosensor/libraries/ArduinoJson/extras/tests/FailingBuilds/read_long_long.cpp
+++ b/neosensor/libraries/ArduinoJson/extras/tests/FailingBuilds/read_long_long.cpp
@@ -1,5 +1,5 @@
 // ArduinoJson - https://arduinojson.org
-// Copyright © 2014-2022, Benoit BLANCHON
+// Copyright © 2014-2023, Benoit BLANCHON
 // MIT License
 
 #define ARDUINOJSON_USE_LONG_LONG 0
@@ -9,10 +9,6 @@
 #  error This test requires sizeof(long) < 8
 #endif
 
-#if !ARDUINOJSON_HAS_LONG_LONG
-#  error This test requires C++11
-#endif
-
 ARDUINOJSON_ASSERT_INTEGER_TYPE_IS_SUPPORTED(long long)
 int main() {
   DynamicJsonDocument doc(1024);
diff --git a/neosensor/libraries/ArduinoJson/extras/tests/FailingBuilds/variant_as_char.cpp b/neosensor/libraries/ArduinoJson/extras/tests/FailingBuilds/variant_as_char.cpp
index 41885cf6..1a9e8342 100644
--- a/neosensor/libraries/ArduinoJson/extras/tests/FailingBuilds/variant_as_char.cpp
+++ b/neosensor/libraries/ArduinoJson/extras/tests/FailingBuilds/variant_as_char.cpp
@@ -1,5 +1,5 @@
 // ArduinoJson - https://arduinojson.org
-// Copyright © 2014-2022, Benoit BLANCHON
+// Copyright © 2014-2023, Benoit BLANCHON
 // MIT License
 
 #include <ArduinoJson.h>
diff --git a/neosensor/libraries/ArduinoJson/extras/tests/FailingBuilds/write_long_long.cpp b/neosensor/libraries/ArduinoJson/extras/tests/FailingBuilds/write_long_long.cpp
index 2213a4dc..6ff83565 100644
--- a/neosensor/libraries/ArduinoJson/extras/tests/FailingBuilds/write_long_long.cpp
+++ b/neosensor/libraries/ArduinoJson/extras/tests/FailingBuilds/write_long_long.cpp
@@ -1,5 +1,5 @@
 // ArduinoJson - https://arduinojson.org
-// Copyright © 2014-2022, Benoit BLANCHON
+// Copyright © 2014-2023, Benoit BLANCHON
 // MIT License
 
 #define ARDUINOJSON_USE_LONG_LONG 0
@@ -9,10 +9,6 @@
 #  error This test requires sizeof(long) < 8
 #endif
 
-#if !ARDUINOJSON_HAS_LONG_LONG
-#  error This test requires C++11
-#endif
-
 int main() {
   DynamicJsonDocument doc(1024);
   doc["dummy"] = static_cast<long long>(42);
diff --git a/neosensor/libraries/ArduinoJson/extras/tests/Helpers/Arduino.h b/neosensor/libraries/ArduinoJson/extras/tests/Helpers/Arduino.h
index 4f24b263..ef3640d9 100644
--- a/neosensor/libraries/ArduinoJson/extras/tests/Helpers/Arduino.h
+++ b/neosensor/libraries/ArduinoJson/extras/tests/Helpers/Arduino.h
@@ -1,5 +1,5 @@
 // ArduinoJson - https://arduinojson.org
-// Copyright © 2014-2022, Benoit BLANCHON
+// Copyright © 2014-2023, Benoit BLANCHON
 // MIT License
 
 #pragma once
@@ -7,6 +7,7 @@
 #include "api/Print.h"
 #include "api/Stream.h"
 #include "api/String.h"
-#include "progmem_emulation.hpp"
+#include "avr/pgmspace.h"
 
+#define ARDUINO
 #define ARDUINO_H_INCLUDED 1
diff --git a/neosensor/libraries/ArduinoJson/extras/tests/Helpers/CustomReader.hpp b/neosensor/libraries/ArduinoJson/extras/tests/Helpers/CustomReader.hpp
index 8a97b503..66525a25 100644
--- a/neosensor/libraries/ArduinoJson/extras/tests/Helpers/CustomReader.hpp
+++ b/neosensor/libraries/ArduinoJson/extras/tests/Helpers/CustomReader.hpp
@@ -1,5 +1,5 @@
 // ArduinoJson - https://arduinojson.org
-// Copyright © 2014-2022, Benoit BLANCHON
+// Copyright © 2014-2023, Benoit BLANCHON
 // MIT License
 
 #pragma once
@@ -7,20 +7,18 @@
 #include <sstream>
 
 class CustomReader {
-  std::stringstream _stream;
+  std::stringstream stream_;
 
  public:
-  CustomReader(const char* input) : _stream(input) {}
+  CustomReader(const char* input) : stream_(input) {}
+  CustomReader(const CustomReader&) = delete;
 
   int read() {
-    return _stream.get();
+    return stream_.get();
   }
 
   size_t readBytes(char* buffer, size_t length) {
-    _stream.read(buffer, static_cast<std::streamsize>(length));
-    return static_cast<size_t>(_stream.gcount());
+    stream_.read(buffer, static_cast<std::streamsize>(length));
+    return static_cast<size_t>(stream_.gcount());
   }
-
- private:
-  CustomReader(const CustomReader&);
 };
diff --git a/neosensor/libraries/ArduinoJson/extras/tests/Helpers/api/Print.h b/neosensor/libraries/ArduinoJson/extras/tests/Helpers/api/Print.h
index 864fb41a..d3c27ccf 100644
--- a/neosensor/libraries/ArduinoJson/extras/tests/Helpers/api/Print.h
+++ b/neosensor/libraries/ArduinoJson/extras/tests/Helpers/api/Print.h
@@ -1,5 +1,5 @@
 // ArduinoJson - https://arduinojson.org
-// Copyright © 2014-2022, Benoit BLANCHON
+// Copyright © 2014-2023, Benoit BLANCHON
 // MIT License
 
 #pragma once
diff --git a/neosensor/libraries/ArduinoJson/extras/tests/Helpers/api/Stream.h b/neosensor/libraries/ArduinoJson/extras/tests/Helpers/api/Stream.h
index 8cc18d2b..387940bb 100644
--- a/neosensor/libraries/ArduinoJson/extras/tests/Helpers/api/Stream.h
+++ b/neosensor/libraries/ArduinoJson/extras/tests/Helpers/api/Stream.h
@@ -1,5 +1,5 @@
 // ArduinoJson - https://arduinojson.org
-// Copyright © 2014-2022, Benoit BLANCHON
+// Copyright © 2014-2023, Benoit BLANCHON
 // MIT License
 
 #pragma once
diff --git a/neosensor/libraries/ArduinoJson/extras/tests/Helpers/api/String.h b/neosensor/libraries/ArduinoJson/extras/tests/Helpers/api/String.h
index e55ea445..64c763f9 100644
--- a/neosensor/libraries/ArduinoJson/extras/tests/Helpers/api/String.h
+++ b/neosensor/libraries/ArduinoJson/extras/tests/Helpers/api/String.h
@@ -1,5 +1,5 @@
 // ArduinoJson - https://arduinojson.org
-// Copyright © 2014-2022, Benoit BLANCHON
+// Copyright © 2014-2023, Benoit BLANCHON
 // MIT License
 
 #pragma once
diff --git a/neosensor/libraries/ArduinoJson/extras/tests/Helpers/progmem_emulation.hpp b/neosensor/libraries/ArduinoJson/extras/tests/Helpers/avr/pgmspace.h
similarity index 76%
rename from neosensor/libraries/ArduinoJson/extras/tests/Helpers/progmem_emulation.hpp
rename to neosensor/libraries/ArduinoJson/extras/tests/Helpers/avr/pgmspace.h
index 41597dca..378065da 100644
--- a/neosensor/libraries/ArduinoJson/extras/tests/Helpers/progmem_emulation.hpp
+++ b/neosensor/libraries/ArduinoJson/extras/tests/Helpers/avr/pgmspace.h
@@ -1,7 +1,9 @@
 // ArduinoJson - https://arduinojson.org
-// Copyright © 2014-2022, Benoit BLANCHON
+// Copyright © 2014-2023, Benoit BLANCHON
 // MIT License
 
+#pragma once
+
 #include <stdint.h>  // uint8_t
 
 #define PROGMEM
@@ -23,7 +25,7 @@ inline uint8_t pgm_read_byte(const void* p) {
   return *reinterpret_cast<const uint8_t*>(convertFlashToPtr(p));
 }
 
-#define ARDUINOJSON_DEFINE_PROGMEM_ARRAY(type, name, value)        \
-  static type const ARDUINOJSON_CONCAT2(name, _progmem)[] = value; \
-  static type const* name = reinterpret_cast<type const*>(         \
+#define ARDUINOJSON_DEFINE_PROGMEM_ARRAY(type, name, ...)                \
+  static type const ARDUINOJSON_CONCAT2(name, _progmem)[] = __VA_ARGS__; \
+  static type const* name = reinterpret_cast<type const*>(               \
       convertPtrToFlash(ARDUINOJSON_CONCAT2(name, _progmem)));
diff --git a/neosensor/libraries/ArduinoJson/extras/tests/IntegrationTests/CMakeLists.txt b/neosensor/libraries/ArduinoJson/extras/tests/IntegrationTests/CMakeLists.txt
index 88b5ba68..9ee858d8 100644
--- a/neosensor/libraries/ArduinoJson/extras/tests/IntegrationTests/CMakeLists.txt
+++ b/neosensor/libraries/ArduinoJson/extras/tests/IntegrationTests/CMakeLists.txt
@@ -1,5 +1,5 @@
 # ArduinoJson - https://arduinojson.org
-# Copyright © 2014-2022, Benoit BLANCHON
+# Copyright © 2014-2023, Benoit BLANCHON
 # MIT License
 
 add_executable(IntegrationTests
@@ -9,10 +9,10 @@ add_executable(IntegrationTests
 	openweathermap.cpp
 )
 
-if(CMAKE_CXX_COMPILER_ID MATCHES "GNU")
+if(CMAKE_CXX_COMPILER_ID MATCHES "GNU" AND CMAKE_CXX_COMPILER_VERSION VERSION_GREATER_EQUAL 6)
 	target_compile_options(IntegrationTests
 		PUBLIC
-		-fsingle-precision-constant # issue 544
+			-fsingle-precision-constant # issue 544
 	)
 endif()
 
@@ -20,5 +20,5 @@ add_test(IntegrationTests IntegrationTests)
 
 set_tests_properties(IntegrationTests
 	PROPERTIES
-		LABELS 		"Catch"
+		LABELS "Catch"
 )
diff --git a/neosensor/libraries/ArduinoJson/extras/tests/IntegrationTests/gbathree.cpp b/neosensor/libraries/ArduinoJson/extras/tests/IntegrationTests/gbathree.cpp
index 2190eb10..34c4d0d8 100644
--- a/neosensor/libraries/ArduinoJson/extras/tests/IntegrationTests/gbathree.cpp
+++ b/neosensor/libraries/ArduinoJson/extras/tests/IntegrationTests/gbathree.cpp
@@ -1,5 +1,5 @@
 // ArduinoJson - https://arduinojson.org
-// Copyright © 2014-2022, Benoit BLANCHON
+// Copyright © 2014-2023, Benoit BLANCHON
 // MIT License
 
 #include <ArduinoJson.h>
diff --git a/neosensor/libraries/ArduinoJson/extras/tests/IntegrationTests/issue772.cpp b/neosensor/libraries/ArduinoJson/extras/tests/IntegrationTests/issue772.cpp
index 4984b43a..dd85a085 100644
--- a/neosensor/libraries/ArduinoJson/extras/tests/IntegrationTests/issue772.cpp
+++ b/neosensor/libraries/ArduinoJson/extras/tests/IntegrationTests/issue772.cpp
@@ -1,5 +1,5 @@
 // ArduinoJson - https://arduinojson.org
-// Copyright © 2014-2022, Benoit BLANCHON
+// Copyright © 2014-2023, Benoit BLANCHON
 // MIT License
 
 #include <ArduinoJson.h>
diff --git a/neosensor/libraries/ArduinoJson/extras/tests/IntegrationTests/openweathermap.cpp b/neosensor/libraries/ArduinoJson/extras/tests/IntegrationTests/openweathermap.cpp
index 44e2f760..e91ac0a8 100644
--- a/neosensor/libraries/ArduinoJson/extras/tests/IntegrationTests/openweathermap.cpp
+++ b/neosensor/libraries/ArduinoJson/extras/tests/IntegrationTests/openweathermap.cpp
@@ -1,5 +1,5 @@
 // ArduinoJson - https://arduinojson.org
-// Copyright © 2014-2022, Benoit BLANCHON
+// Copyright © 2014-2023, Benoit BLANCHON
 // MIT License
 
 #include <ArduinoJson.h>
diff --git a/neosensor/libraries/ArduinoJson/extras/tests/IntegrationTests/round_trip.cpp b/neosensor/libraries/ArduinoJson/extras/tests/IntegrationTests/round_trip.cpp
index edc55385..ede311d0 100644
--- a/neosensor/libraries/ArduinoJson/extras/tests/IntegrationTests/round_trip.cpp
+++ b/neosensor/libraries/ArduinoJson/extras/tests/IntegrationTests/round_trip.cpp
@@ -1,5 +1,5 @@
 // ArduinoJson - https://arduinojson.org
-// Copyright © 2014-2022, Benoit BLANCHON
+// Copyright © 2014-2023, Benoit BLANCHON
 // MIT License
 
 #include <ArduinoJson.h>
diff --git a/neosensor/libraries/ArduinoJson/extras/tests/JsonArray/CMakeLists.txt b/neosensor/libraries/ArduinoJson/extras/tests/JsonArray/CMakeLists.txt
index 9f897992..d97a2b16 100644
--- a/neosensor/libraries/ArduinoJson/extras/tests/JsonArray/CMakeLists.txt
+++ b/neosensor/libraries/ArduinoJson/extras/tests/JsonArray/CMakeLists.txt
@@ -1,5 +1,5 @@
 # ArduinoJson - https://arduinojson.org
-# Copyright © 2014-2022, Benoit BLANCHON
+# Copyright © 2014-2023, Benoit BLANCHON
 # MIT License
 
 add_executable(JsonArrayTests
@@ -24,5 +24,5 @@ add_test(JsonArray JsonArrayTests)
 
 set_tests_properties(JsonArray
 	PROPERTIES
-		LABELS 		"Catch"
+		LABELS "Catch"
 )
diff --git a/neosensor/libraries/ArduinoJson/extras/tests/JsonArray/add.cpp b/neosensor/libraries/ArduinoJson/extras/tests/JsonArray/add.cpp
index 369bcf2d..2708c7d4 100644
--- a/neosensor/libraries/ArduinoJson/extras/tests/JsonArray/add.cpp
+++ b/neosensor/libraries/ArduinoJson/extras/tests/JsonArray/add.cpp
@@ -1,5 +1,5 @@
 // ArduinoJson - https://arduinojson.org
-// Copyright © 2014-2022, Benoit BLANCHON
+// Copyright © 2014-2023, Benoit BLANCHON
 // MIT License
 
 #include <ArduinoJson.h>
diff --git a/neosensor/libraries/ArduinoJson/extras/tests/JsonArray/clear.cpp b/neosensor/libraries/ArduinoJson/extras/tests/JsonArray/clear.cpp
index baeb0222..3a593c5a 100644
--- a/neosensor/libraries/ArduinoJson/extras/tests/JsonArray/clear.cpp
+++ b/neosensor/libraries/ArduinoJson/extras/tests/JsonArray/clear.cpp
@@ -1,5 +1,5 @@
 // ArduinoJson - https://arduinojson.org
-// Copyright © 2014-2022, Benoit BLANCHON
+// Copyright © 2014-2023, Benoit BLANCHON
 // MIT License
 
 #include <ArduinoJson.h>
diff --git a/neosensor/libraries/ArduinoJson/extras/tests/JsonArray/compare.cpp b/neosensor/libraries/ArduinoJson/extras/tests/JsonArray/compare.cpp
index 4d1c9d07..1e64e166 100644
--- a/neosensor/libraries/ArduinoJson/extras/tests/JsonArray/compare.cpp
+++ b/neosensor/libraries/ArduinoJson/extras/tests/JsonArray/compare.cpp
@@ -1,5 +1,5 @@
 // ArduinoJson - https://arduinojson.org
-// Copyright © 2014-2022, Benoit BLANCHON
+// Copyright © 2014-2023, Benoit BLANCHON
 // MIT License
 
 #include <ArduinoJson.h>
diff --git a/neosensor/libraries/ArduinoJson/extras/tests/JsonArray/copyArray.cpp b/neosensor/libraries/ArduinoJson/extras/tests/JsonArray/copyArray.cpp
index 599821df..7973c375 100644
--- a/neosensor/libraries/ArduinoJson/extras/tests/JsonArray/copyArray.cpp
+++ b/neosensor/libraries/ArduinoJson/extras/tests/JsonArray/copyArray.cpp
@@ -1,5 +1,5 @@
 // ArduinoJson - https://arduinojson.org
-// Copyright © 2014-2022, Benoit BLANCHON
+// Copyright © 2014-2023, Benoit BLANCHON
 // MIT License
 
 #include <ArduinoJson.h>
@@ -165,7 +165,7 @@ TEST_CASE("copyArray()") {
     char json[32] = "";
     int source[][3] = {{1, 2, 3}, {4, 5, 6}};
 
-    CAPTURE(SIZE)
+    CAPTURE(SIZE);
 
     bool ok = copyArray(source, array);
     CAPTURE(doc.memoryUsage());
diff --git a/neosensor/libraries/ArduinoJson/extras/tests/JsonArray/createNested.cpp b/neosensor/libraries/ArduinoJson/extras/tests/JsonArray/createNested.cpp
index dbc4c304..4801dbe0 100644
--- a/neosensor/libraries/ArduinoJson/extras/tests/JsonArray/createNested.cpp
+++ b/neosensor/libraries/ArduinoJson/extras/tests/JsonArray/createNested.cpp
@@ -1,5 +1,5 @@
 // ArduinoJson - https://arduinojson.org
-// Copyright © 2014-2022, Benoit BLANCHON
+// Copyright © 2014-2023, Benoit BLANCHON
 // MIT License
 
 #include <ArduinoJson.h>
diff --git a/neosensor/libraries/ArduinoJson/extras/tests/JsonArray/equals.cpp b/neosensor/libraries/ArduinoJson/extras/tests/JsonArray/equals.cpp
index f4f9b521..4998d388 100644
--- a/neosensor/libraries/ArduinoJson/extras/tests/JsonArray/equals.cpp
+++ b/neosensor/libraries/ArduinoJson/extras/tests/JsonArray/equals.cpp
@@ -1,5 +1,5 @@
 // ArduinoJson - https://arduinojson.org
-// Copyright © 2014-2022, Benoit BLANCHON
+// Copyright © 2014-2023, Benoit BLANCHON
 // MIT License
 
 #include <ArduinoJson.h>
diff --git a/neosensor/libraries/ArduinoJson/extras/tests/JsonArray/isNull.cpp b/neosensor/libraries/ArduinoJson/extras/tests/JsonArray/isNull.cpp
index ad953b91..0da13be8 100644
--- a/neosensor/libraries/ArduinoJson/extras/tests/JsonArray/isNull.cpp
+++ b/neosensor/libraries/ArduinoJson/extras/tests/JsonArray/isNull.cpp
@@ -1,5 +1,5 @@
 // ArduinoJson - https://arduinojson.org
-// Copyright © 2014-2022, Benoit BLANCHON
+// Copyright © 2014-2023, Benoit BLANCHON
 // MIT License
 
 #include <ArduinoJson.h>
diff --git a/neosensor/libraries/ArduinoJson/extras/tests/JsonArray/iterator.cpp b/neosensor/libraries/ArduinoJson/extras/tests/JsonArray/iterator.cpp
index 435a5bb9..50eb2763 100644
--- a/neosensor/libraries/ArduinoJson/extras/tests/JsonArray/iterator.cpp
+++ b/neosensor/libraries/ArduinoJson/extras/tests/JsonArray/iterator.cpp
@@ -1,5 +1,5 @@
 // ArduinoJson - https://arduinojson.org
-// Copyright © 2014-2022, Benoit BLANCHON
+// Copyright © 2014-2023, Benoit BLANCHON
 // MIT License
 
 #include <ArduinoJson.h>
diff --git a/neosensor/libraries/ArduinoJson/extras/tests/JsonArray/memoryUsage.cpp b/neosensor/libraries/ArduinoJson/extras/tests/JsonArray/memoryUsage.cpp
index 05b743f1..9f4ea09b 100644
--- a/neosensor/libraries/ArduinoJson/extras/tests/JsonArray/memoryUsage.cpp
+++ b/neosensor/libraries/ArduinoJson/extras/tests/JsonArray/memoryUsage.cpp
@@ -1,5 +1,5 @@
 // ArduinoJson - https://arduinojson.org
-// Copyright © 2014-2022, Benoit BLANCHON
+// Copyright © 2014-2023, Benoit BLANCHON
 // MIT License
 
 #include <ArduinoJson.h>
diff --git a/neosensor/libraries/ArduinoJson/extras/tests/JsonArray/nesting.cpp b/neosensor/libraries/ArduinoJson/extras/tests/JsonArray/nesting.cpp
index 2118f5f7..fc1be8c6 100644
--- a/neosensor/libraries/ArduinoJson/extras/tests/JsonArray/nesting.cpp
+++ b/neosensor/libraries/ArduinoJson/extras/tests/JsonArray/nesting.cpp
@@ -1,5 +1,5 @@
 // ArduinoJson - https://arduinojson.org
-// Copyright © 2014-2022, Benoit BLANCHON
+// Copyright © 2014-2023, Benoit BLANCHON
 // MIT License
 
 #include <ArduinoJson.h>
diff --git a/neosensor/libraries/ArduinoJson/extras/tests/JsonArray/remove.cpp b/neosensor/libraries/ArduinoJson/extras/tests/JsonArray/remove.cpp
index b9e86fdd..f75efda3 100644
--- a/neosensor/libraries/ArduinoJson/extras/tests/JsonArray/remove.cpp
+++ b/neosensor/libraries/ArduinoJson/extras/tests/JsonArray/remove.cpp
@@ -1,5 +1,5 @@
 // ArduinoJson - https://arduinojson.org
-// Copyright © 2014-2022, Benoit BLANCHON
+// Copyright © 2014-2023, Benoit BLANCHON
 // MIT License
 
 #include <ArduinoJson.h>
diff --git a/neosensor/libraries/ArduinoJson/extras/tests/JsonArray/size.cpp b/neosensor/libraries/ArduinoJson/extras/tests/JsonArray/size.cpp
index 68b10de7..6edd4db8 100644
--- a/neosensor/libraries/ArduinoJson/extras/tests/JsonArray/size.cpp
+++ b/neosensor/libraries/ArduinoJson/extras/tests/JsonArray/size.cpp
@@ -1,5 +1,5 @@
 // ArduinoJson - https://arduinojson.org
-// Copyright © 2014-2022, Benoit BLANCHON
+// Copyright © 2014-2023, Benoit BLANCHON
 // MIT License
 
 #include <ArduinoJson.h>
diff --git a/neosensor/libraries/ArduinoJson/extras/tests/JsonArray/std_string.cpp b/neosensor/libraries/ArduinoJson/extras/tests/JsonArray/std_string.cpp
index 7af0ac2e..7d3ec9f4 100644
--- a/neosensor/libraries/ArduinoJson/extras/tests/JsonArray/std_string.cpp
+++ b/neosensor/libraries/ArduinoJson/extras/tests/JsonArray/std_string.cpp
@@ -1,5 +1,5 @@
 // ArduinoJson - https://arduinojson.org
-// Copyright © 2014-2022, Benoit BLANCHON
+// Copyright © 2014-2023, Benoit BLANCHON
 // MIT License
 
 #include <ArduinoJson.h>
diff --git a/neosensor/libraries/ArduinoJson/extras/tests/JsonArray/subscript.cpp b/neosensor/libraries/ArduinoJson/extras/tests/JsonArray/subscript.cpp
index 57bbe140..3079d2f9 100644
--- a/neosensor/libraries/ArduinoJson/extras/tests/JsonArray/subscript.cpp
+++ b/neosensor/libraries/ArduinoJson/extras/tests/JsonArray/subscript.cpp
@@ -1,5 +1,5 @@
 // ArduinoJson - https://arduinojson.org
-// Copyright © 2014-2022, Benoit BLANCHON
+// Copyright © 2014-2023, Benoit BLANCHON
 // MIT License
 
 #include <ArduinoJson.h>
diff --git a/neosensor/libraries/ArduinoJson/extras/tests/JsonArray/unbound.cpp b/neosensor/libraries/ArduinoJson/extras/tests/JsonArray/unbound.cpp
index 792feec3..0e21310b 100644
--- a/neosensor/libraries/ArduinoJson/extras/tests/JsonArray/unbound.cpp
+++ b/neosensor/libraries/ArduinoJson/extras/tests/JsonArray/unbound.cpp
@@ -1,5 +1,5 @@
 // ArduinoJson - https://arduinojson.org
-// Copyright © 2014-2022, Benoit BLANCHON
+// Copyright © 2014-2023, Benoit BLANCHON
 // MIT License
 
 #include <ArduinoJson.h>
diff --git a/neosensor/libraries/ArduinoJson/extras/tests/JsonDeserializer/CMakeLists.txt b/neosensor/libraries/ArduinoJson/extras/tests/JsonDeserializer/CMakeLists.txt
index 429c3755..f60ad416 100644
--- a/neosensor/libraries/ArduinoJson/extras/tests/JsonDeserializer/CMakeLists.txt
+++ b/neosensor/libraries/ArduinoJson/extras/tests/JsonDeserializer/CMakeLists.txt
@@ -1,5 +1,5 @@
 # ArduinoJson - https://arduinojson.org
-# Copyright © 2014-2022, Benoit BLANCHON
+# Copyright © 2014-2023, Benoit BLANCHON
 # MIT License
 
 add_executable(JsonDeserializerTests
@@ -24,5 +24,5 @@ add_test(JsonDeserializer JsonDeserializerTests)
 
 set_tests_properties(JsonDeserializer
 	PROPERTIES
-		LABELS 		"Catch"
+		LABELS "Catch"
 )
diff --git a/neosensor/libraries/ArduinoJson/extras/tests/JsonDeserializer/DeserializationError.cpp b/neosensor/libraries/ArduinoJson/extras/tests/JsonDeserializer/DeserializationError.cpp
index 01ddaf69..fd1b1885 100644
--- a/neosensor/libraries/ArduinoJson/extras/tests/JsonDeserializer/DeserializationError.cpp
+++ b/neosensor/libraries/ArduinoJson/extras/tests/JsonDeserializer/DeserializationError.cpp
@@ -1,10 +1,12 @@
 // ArduinoJson - https://arduinojson.org
-// Copyright © 2014-2022, Benoit BLANCHON
+// Copyright © 2014-2023, Benoit BLANCHON
 // MIT License
 
 #include <ArduinoJson.h>
 #include <catch.hpp>
 
+#include <sstream>
+
 void testStringification(DeserializationError error, std::string expected) {
   REQUIRE(error.c_str() == expected);
 }
diff --git a/neosensor/libraries/ArduinoJson/extras/tests/JsonDeserializer/array.cpp b/neosensor/libraries/ArduinoJson/extras/tests/JsonDeserializer/array.cpp
index 2e0038ee..b30269c1 100644
--- a/neosensor/libraries/ArduinoJson/extras/tests/JsonDeserializer/array.cpp
+++ b/neosensor/libraries/ArduinoJson/extras/tests/JsonDeserializer/array.cpp
@@ -1,5 +1,5 @@
 // ArduinoJson - https://arduinojson.org
-// Copyright © 2014-2022, Benoit BLANCHON
+// Copyright © 2014-2023, Benoit BLANCHON
 // MIT License
 
 #include <ArduinoJson.h>
diff --git a/neosensor/libraries/ArduinoJson/extras/tests/JsonDeserializer/array_static.cpp b/neosensor/libraries/ArduinoJson/extras/tests/JsonDeserializer/array_static.cpp
index d828518f..2d1f538d 100644
--- a/neosensor/libraries/ArduinoJson/extras/tests/JsonDeserializer/array_static.cpp
+++ b/neosensor/libraries/ArduinoJson/extras/tests/JsonDeserializer/array_static.cpp
@@ -1,5 +1,5 @@
 // ArduinoJson - https://arduinojson.org
-// Copyright © 2014-2022, Benoit BLANCHON
+// Copyright © 2014-2023, Benoit BLANCHON
 // MIT License
 
 #include <ArduinoJson.h>
diff --git a/neosensor/libraries/ArduinoJson/extras/tests/JsonDeserializer/filter.cpp b/neosensor/libraries/ArduinoJson/extras/tests/JsonDeserializer/filter.cpp
index e0c37341..f1c6e2e4 100644
--- a/neosensor/libraries/ArduinoJson/extras/tests/JsonDeserializer/filter.cpp
+++ b/neosensor/libraries/ArduinoJson/extras/tests/JsonDeserializer/filter.cpp
@@ -1,5 +1,5 @@
 // ArduinoJson - https://arduinojson.org
-// Copyright © 2014-2022, Benoit BLANCHON
+// Copyright © 2014-2023, Benoit BLANCHON
 // MIT License
 
 #define ARDUINOJSON_ENABLE_COMMENTS 1
diff --git a/neosensor/libraries/ArduinoJson/extras/tests/JsonDeserializer/incomplete_input.cpp b/neosensor/libraries/ArduinoJson/extras/tests/JsonDeserializer/incomplete_input.cpp
index 4e5bcdbd..a34aede6 100644
--- a/neosensor/libraries/ArduinoJson/extras/tests/JsonDeserializer/incomplete_input.cpp
+++ b/neosensor/libraries/ArduinoJson/extras/tests/JsonDeserializer/incomplete_input.cpp
@@ -1,5 +1,5 @@
 // ArduinoJson - https://arduinojson.org
-// Copyright © 2014-2022, Benoit BLANCHON
+// Copyright © 2014-2023, Benoit BLANCHON
 // MIT License
 
 #define ARDUINOJSON_DECODE_UNICODE 1
diff --git a/neosensor/libraries/ArduinoJson/extras/tests/JsonDeserializer/input_types.cpp b/neosensor/libraries/ArduinoJson/extras/tests/JsonDeserializer/input_types.cpp
index 04054c6a..788319f4 100644
--- a/neosensor/libraries/ArduinoJson/extras/tests/JsonDeserializer/input_types.cpp
+++ b/neosensor/libraries/ArduinoJson/extras/tests/JsonDeserializer/input_types.cpp
@@ -1,5 +1,5 @@
 // ArduinoJson - https://arduinojson.org
-// Copyright © 2014-2022, Benoit BLANCHON
+// Copyright © 2014-2023, Benoit BLANCHON
 // MIT License
 
 #include <ArduinoJson.h>
@@ -24,6 +24,30 @@ TEST_CASE("deserializeJson(char*)") {
   }
 }
 
+TEST_CASE("deserializeJson(unsigned char*, unsigned int)") {  // issue #1897
+  StaticJsonDocument<1024> doc;
+
+  unsigned char input[] = "{\"hello\":\"world\"}";
+  unsigned char* input_ptr = input;
+  unsigned int size = sizeof(input);
+
+  DeserializationError err = deserializeJson(doc, input_ptr, size);
+
+  REQUIRE(err == DeserializationError::Ok);
+}
+
+TEST_CASE("deserializeJson(uint8_t*, size_t)") {  // issue #1898
+  StaticJsonDocument<1024> doc;
+
+  uint8_t input[] = "{\"hello\":\"world\"}";
+  uint8_t* input_ptr = input;
+  size_t size = sizeof(input);
+
+  DeserializationError err = deserializeJson(doc, input_ptr, size);
+
+  REQUIRE(err == DeserializationError::Ok);
+}
+
 TEST_CASE("deserializeJson(const std::string&)") {
   DynamicJsonDocument doc(4096);
 
diff --git a/neosensor/libraries/ArduinoJson/extras/tests/JsonDeserializer/invalid_input.cpp b/neosensor/libraries/ArduinoJson/extras/tests/JsonDeserializer/invalid_input.cpp
index a2304a37..52b50383 100644
--- a/neosensor/libraries/ArduinoJson/extras/tests/JsonDeserializer/invalid_input.cpp
+++ b/neosensor/libraries/ArduinoJson/extras/tests/JsonDeserializer/invalid_input.cpp
@@ -1,5 +1,5 @@
 // ArduinoJson - https://arduinojson.org
-// Copyright © 2014-2022, Benoit BLANCHON
+// Copyright © 2014-2023, Benoit BLANCHON
 // MIT License
 
 #define ARDUINOJSON_DECODE_UNICODE 1
diff --git a/neosensor/libraries/ArduinoJson/extras/tests/JsonDeserializer/misc.cpp b/neosensor/libraries/ArduinoJson/extras/tests/JsonDeserializer/misc.cpp
index dc8083c4..4f22de15 100644
--- a/neosensor/libraries/ArduinoJson/extras/tests/JsonDeserializer/misc.cpp
+++ b/neosensor/libraries/ArduinoJson/extras/tests/JsonDeserializer/misc.cpp
@@ -1,5 +1,5 @@
 // ArduinoJson - https://arduinojson.org
-// Copyright © 2014-2022, Benoit BLANCHON
+// Copyright © 2014-2023, Benoit BLANCHON
 // MIT License
 
 #include <ArduinoJson.h>
diff --git a/neosensor/libraries/ArduinoJson/extras/tests/JsonDeserializer/nestingLimit.cpp b/neosensor/libraries/ArduinoJson/extras/tests/JsonDeserializer/nestingLimit.cpp
index 0cdcb3e5..cf55dc85 100644
--- a/neosensor/libraries/ArduinoJson/extras/tests/JsonDeserializer/nestingLimit.cpp
+++ b/neosensor/libraries/ArduinoJson/extras/tests/JsonDeserializer/nestingLimit.cpp
@@ -1,10 +1,12 @@
 // ArduinoJson - https://arduinojson.org
-// Copyright © 2014-2022, Benoit BLANCHON
+// Copyright © 2014-2023, Benoit BLANCHON
 // MIT License
 
 #include <ArduinoJson.h>
 #include <catch.hpp>
 
+#include <sstream>
+
 #define SHOULD_WORK(expression) REQUIRE(DeserializationError::Ok == expression);
 #define SHOULD_FAIL(expression) \
   REQUIRE(DeserializationError::TooDeep == expression);
diff --git a/neosensor/libraries/ArduinoJson/extras/tests/JsonDeserializer/number.cpp b/neosensor/libraries/ArduinoJson/extras/tests/JsonDeserializer/number.cpp
index 44e24cbe..05cf89e8 100644
--- a/neosensor/libraries/ArduinoJson/extras/tests/JsonDeserializer/number.cpp
+++ b/neosensor/libraries/ArduinoJson/extras/tests/JsonDeserializer/number.cpp
@@ -1,5 +1,5 @@
 // ArduinoJson - https://arduinojson.org
-// Copyright © 2014-2022, Benoit BLANCHON
+// Copyright © 2014-2023, Benoit BLANCHON
 // MIT License
 
 #define ARDUINOJSON_USE_LONG_LONG 0
@@ -11,8 +11,8 @@
 #include <catch.hpp>
 
 namespace my {
-using ARDUINOJSON_NAMESPACE::isinf;
-using ARDUINOJSON_NAMESPACE::isnan;
+using ArduinoJson::detail::isinf;
+using ArduinoJson::detail::isnan;
 }  // namespace my
 
 TEST_CASE("deserialize an integer") {
diff --git a/neosensor/libraries/ArduinoJson/extras/tests/JsonDeserializer/object.cpp b/neosensor/libraries/ArduinoJson/extras/tests/JsonDeserializer/object.cpp
index e5d5de7f..814258ec 100644
--- a/neosensor/libraries/ArduinoJson/extras/tests/JsonDeserializer/object.cpp
+++ b/neosensor/libraries/ArduinoJson/extras/tests/JsonDeserializer/object.cpp
@@ -1,5 +1,5 @@
 // ArduinoJson - https://arduinojson.org
-// Copyright © 2014-2022, Benoit BLANCHON
+// Copyright © 2014-2023, Benoit BLANCHON
 // MIT License
 
 #include <ArduinoJson.h>
@@ -100,7 +100,7 @@ TEST_CASE("deserialize JSON object") {
       REQUIRE(obj["key"] == "value");
     }
 
-    SECTION("Before the colon") {
+    SECTION("Before the comma") {
       DeserializationError err =
           deserializeJson(doc, "{\"key1\":\"value1\" ,\"key2\":\"value2\"}");
       JsonObject obj = doc.as<JsonObject>();
@@ -112,9 +112,9 @@ TEST_CASE("deserialize JSON object") {
       REQUIRE(obj["key2"] == "value2");
     }
 
-    SECTION("After the colon") {
+    SECTION("After the comma") {
       DeserializationError err =
-          deserializeJson(doc, "{\"key1\":\"value1\" ,\"key2\":\"value2\"}");
+          deserializeJson(doc, "{\"key1\":\"value1\", \"key2\":\"value2\"}");
       JsonObject obj = doc.as<JsonObject>();
 
       REQUIRE(err == DeserializationError::Ok);
diff --git a/neosensor/libraries/ArduinoJson/extras/tests/JsonDeserializer/object_static.cpp b/neosensor/libraries/ArduinoJson/extras/tests/JsonDeserializer/object_static.cpp
index a326afe6..b198ea74 100644
--- a/neosensor/libraries/ArduinoJson/extras/tests/JsonDeserializer/object_static.cpp
+++ b/neosensor/libraries/ArduinoJson/extras/tests/JsonDeserializer/object_static.cpp
@@ -1,5 +1,5 @@
 // ArduinoJson - https://arduinojson.org
-// Copyright © 2014-2022, Benoit BLANCHON
+// Copyright © 2014-2023, Benoit BLANCHON
 // MIT License
 
 #include <ArduinoJson.h>
diff --git a/neosensor/libraries/ArduinoJson/extras/tests/JsonDeserializer/string.cpp b/neosensor/libraries/ArduinoJson/extras/tests/JsonDeserializer/string.cpp
index 8f93ac63..2379d455 100644
--- a/neosensor/libraries/ArduinoJson/extras/tests/JsonDeserializer/string.cpp
+++ b/neosensor/libraries/ArduinoJson/extras/tests/JsonDeserializer/string.cpp
@@ -1,5 +1,5 @@
 // ArduinoJson - https://arduinojson.org
-// Copyright © 2014-2022, Benoit BLANCHON
+// Copyright © 2014-2023, Benoit BLANCHON
 // MIT License
 
 #define ARDUINOJSON_DECODE_UNICODE 1
diff --git a/neosensor/libraries/ArduinoJson/extras/tests/JsonDocument/BasicJsonDocument.cpp b/neosensor/libraries/ArduinoJson/extras/tests/JsonDocument/BasicJsonDocument.cpp
index d2074401..348fd94c 100644
--- a/neosensor/libraries/ArduinoJson/extras/tests/JsonDocument/BasicJsonDocument.cpp
+++ b/neosensor/libraries/ArduinoJson/extras/tests/JsonDocument/BasicJsonDocument.cpp
@@ -1,5 +1,5 @@
 // ArduinoJson - https://arduinojson.org
-// Copyright © 2014-2022, Benoit BLANCHON
+// Copyright © 2014-2023, Benoit BLANCHON
 // MIT License
 
 #include <ArduinoJson.h>
@@ -8,34 +8,31 @@
 #include <sstream>
 #include <utility>
 
-using ARDUINOJSON_NAMESPACE::addPadding;
-
 class SpyingAllocator {
  public:
-  SpyingAllocator(const SpyingAllocator& src) : _log(src._log) {}
-  SpyingAllocator(std::ostream& log) : _log(log) {}
+  SpyingAllocator(const SpyingAllocator& src) : log_(src.log_) {}
+  SpyingAllocator(std::ostream& log) : log_(log) {}
+  SpyingAllocator& operator=(const SpyingAllocator& src) = delete;
 
   void* allocate(size_t n) {
-    _log << "A" << n;
+    log_ << "A" << n;
     return malloc(n);
   }
   void deallocate(void* p) {
-    _log << "F";
+    log_ << "F";
     free(p);
   }
 
  private:
-  SpyingAllocator& operator=(const SpyingAllocator& src);
-
-  std::ostream& _log;
+  std::ostream& log_;
 };
 
 class ControllableAllocator {
  public:
-  ControllableAllocator() : _enabled(true) {}
+  ControllableAllocator() : enabled_(true) {}
 
   void* allocate(size_t n) {
-    return _enabled ? malloc(n) : 0;
+    return enabled_ ? malloc(n) : 0;
   }
 
   void deallocate(void* p) {
@@ -43,11 +40,11 @@ class ControllableAllocator {
   }
 
   void disable() {
-    _enabled = false;
+    enabled_ = false;
   }
 
  private:
-  bool _enabled;
+  bool enabled_;
 };
 
 TEST_CASE("BasicJsonDocument") {
@@ -72,7 +69,6 @@ TEST_CASE("BasicJsonDocument") {
     REQUIRE(log.str() == "A4096A4096FF");
   }
 
-#if ARDUINOJSON_HAS_RVALUE_REFERENCES
   SECTION("Move construct") {
     {
       BasicJsonDocument<SpyingAllocator> doc1(4096, log);
@@ -87,7 +83,6 @@ TEST_CASE("BasicJsonDocument") {
     }
     REQUIRE(log.str() == "A4096F");
   }
-#endif
 
   SECTION("Copy assign larger") {
     {
@@ -134,7 +129,6 @@ TEST_CASE("BasicJsonDocument") {
     REQUIRE(log.str() == "A1024A1024FF");
   }
 
-#if ARDUINOJSON_HAS_RVALUE_REFERENCES
   SECTION("Move assign") {
     {
       BasicJsonDocument<SpyingAllocator> doc1(4096, log);
@@ -150,7 +144,6 @@ TEST_CASE("BasicJsonDocument") {
     }
     REQUIRE(log.str() == "A4096A8FF");
   }
-#endif
 
   SECTION("garbageCollect()") {
     BasicJsonDocument<ControllableAllocator> doc(4096);
diff --git a/neosensor/libraries/ArduinoJson/extras/tests/JsonDocument/CMakeLists.txt b/neosensor/libraries/ArduinoJson/extras/tests/JsonDocument/CMakeLists.txt
index 53567803..3e9b4683 100644
--- a/neosensor/libraries/ArduinoJson/extras/tests/JsonDocument/CMakeLists.txt
+++ b/neosensor/libraries/ArduinoJson/extras/tests/JsonDocument/CMakeLists.txt
@@ -1,5 +1,5 @@
 # ArduinoJson - https://arduinojson.org
-# Copyright © 2014-2022, Benoit BLANCHON
+# Copyright © 2014-2023, Benoit BLANCHON
 # MIT License
 
 add_executable(JsonDocumentTests
@@ -12,6 +12,7 @@ add_executable(JsonDocumentTests
 	DynamicJsonDocument.cpp
 	ElementProxy.cpp
 	isNull.cpp
+	issue1120.cpp
 	MemberProxy.cpp
 	nesting.cpp
 	overflowed.cpp
@@ -27,5 +28,5 @@ add_test(JsonDocument JsonDocumentTests)
 
 set_tests_properties(JsonDocument
 	PROPERTIES
-		LABELS 		"Catch"
+		LABELS "Catch"
 )
diff --git a/neosensor/libraries/ArduinoJson/extras/tests/JsonDocument/DynamicJsonDocument.cpp b/neosensor/libraries/ArduinoJson/extras/tests/JsonDocument/DynamicJsonDocument.cpp
index ce050f17..397f8c5d 100644
--- a/neosensor/libraries/ArduinoJson/extras/tests/JsonDocument/DynamicJsonDocument.cpp
+++ b/neosensor/libraries/ArduinoJson/extras/tests/JsonDocument/DynamicJsonDocument.cpp
@@ -1,11 +1,11 @@
 // ArduinoJson - https://arduinojson.org
-// Copyright © 2014-2022, Benoit BLANCHON
+// Copyright © 2014-2023, Benoit BLANCHON
 // MIT License
 
 #include <ArduinoJson.h>
 #include <catch.hpp>
 
-using ARDUINOJSON_NAMESPACE::addPadding;
+using ArduinoJson::detail::addPadding;
 
 static void REQUIRE_JSON(JsonDocument& doc, const std::string& expected) {
   std::string json;
diff --git a/neosensor/libraries/ArduinoJson/extras/tests/JsonDocument/ElementProxy.cpp b/neosensor/libraries/ArduinoJson/extras/tests/JsonDocument/ElementProxy.cpp
index eafc85f3..00fcdda2 100644
--- a/neosensor/libraries/ArduinoJson/extras/tests/JsonDocument/ElementProxy.cpp
+++ b/neosensor/libraries/ArduinoJson/extras/tests/JsonDocument/ElementProxy.cpp
@@ -1,11 +1,11 @@
 // ArduinoJson - https://arduinojson.org
-// Copyright © 2014-2022, Benoit BLANCHON
+// Copyright © 2014-2023, Benoit BLANCHON
 // MIT License
 
 #include <ArduinoJson.h>
 #include <catch.hpp>
 
-typedef ARDUINOJSON_NAMESPACE::ElementProxy<JsonDocument&> ElementProxy;
+typedef ArduinoJson::detail::ElementProxy<JsonDocument&> ElementProxy;
 
 TEST_CASE("ElementProxy::add()") {
   DynamicJsonDocument doc(4096);
diff --git a/neosensor/libraries/ArduinoJson/extras/tests/JsonDocument/MemberProxy.cpp b/neosensor/libraries/ArduinoJson/extras/tests/JsonDocument/MemberProxy.cpp
index ed3e32bf..a3707b03 100644
--- a/neosensor/libraries/ArduinoJson/extras/tests/JsonDocument/MemberProxy.cpp
+++ b/neosensor/libraries/ArduinoJson/extras/tests/JsonDocument/MemberProxy.cpp
@@ -1,11 +1,11 @@
 // ArduinoJson - https://arduinojson.org
-// Copyright © 2014-2022, Benoit BLANCHON
+// Copyright © 2014-2023, Benoit BLANCHON
 // MIT License
 
 #include <ArduinoJson.h>
 #include <catch.hpp>
 
-typedef ARDUINOJSON_NAMESPACE::MemberProxy<JsonDocument&, const char*>
+typedef ArduinoJson::detail::MemberProxy<JsonDocument&, const char*>
     MemberProxy;
 
 TEST_CASE("MemberProxy::add()") {
diff --git a/neosensor/libraries/ArduinoJson/extras/tests/JsonDocument/StaticJsonDocument.cpp b/neosensor/libraries/ArduinoJson/extras/tests/JsonDocument/StaticJsonDocument.cpp
index fe193e4b..0d4089bc 100644
--- a/neosensor/libraries/ArduinoJson/extras/tests/JsonDocument/StaticJsonDocument.cpp
+++ b/neosensor/libraries/ArduinoJson/extras/tests/JsonDocument/StaticJsonDocument.cpp
@@ -1,5 +1,5 @@
 // ArduinoJson - https://arduinojson.org
-// Copyright © 2014-2022, Benoit BLANCHON
+// Copyright © 2014-2023, Benoit BLANCHON
 // MIT License
 
 #include <ArduinoJson.h>
diff --git a/neosensor/libraries/ArduinoJson/extras/tests/JsonDocument/add.cpp b/neosensor/libraries/ArduinoJson/extras/tests/JsonDocument/add.cpp
index 109bd08b..5843ac96 100644
--- a/neosensor/libraries/ArduinoJson/extras/tests/JsonDocument/add.cpp
+++ b/neosensor/libraries/ArduinoJson/extras/tests/JsonDocument/add.cpp
@@ -1,5 +1,5 @@
 // ArduinoJson - https://arduinojson.org
-// Copyright © 2014-2022, Benoit BLANCHON
+// Copyright © 2014-2023, Benoit BLANCHON
 // MIT License
 
 #include <ArduinoJson.h>
diff --git a/neosensor/libraries/ArduinoJson/extras/tests/JsonDocument/cast.cpp b/neosensor/libraries/ArduinoJson/extras/tests/JsonDocument/cast.cpp
index e538f4bb..253e1fc7 100644
--- a/neosensor/libraries/ArduinoJson/extras/tests/JsonDocument/cast.cpp
+++ b/neosensor/libraries/ArduinoJson/extras/tests/JsonDocument/cast.cpp
@@ -1,5 +1,5 @@
 // ArduinoJson - https://arduinojson.org
-// Copyright © 2014-2022, Benoit BLANCHON
+// Copyright © 2014-2023, Benoit BLANCHON
 // MIT License
 
 #include <ArduinoJson.h>
diff --git a/neosensor/libraries/ArduinoJson/extras/tests/JsonDocument/compare.cpp b/neosensor/libraries/ArduinoJson/extras/tests/JsonDocument/compare.cpp
index 7e9b9500..d834b226 100644
--- a/neosensor/libraries/ArduinoJson/extras/tests/JsonDocument/compare.cpp
+++ b/neosensor/libraries/ArduinoJson/extras/tests/JsonDocument/compare.cpp
@@ -1,5 +1,5 @@
 // ArduinoJson - https://arduinojson.org
-// Copyright © 2014-2022, Benoit BLANCHON
+// Copyright © 2014-2023, Benoit BLANCHON
 // MIT License
 
 #include <ArduinoJson.h>
diff --git a/neosensor/libraries/ArduinoJson/extras/tests/JsonDocument/containsKey.cpp b/neosensor/libraries/ArduinoJson/extras/tests/JsonDocument/containsKey.cpp
index 69360437..8274fc76 100644
--- a/neosensor/libraries/ArduinoJson/extras/tests/JsonDocument/containsKey.cpp
+++ b/neosensor/libraries/ArduinoJson/extras/tests/JsonDocument/containsKey.cpp
@@ -1,5 +1,5 @@
 // ArduinoJson - https://arduinojson.org
-// Copyright © 2014-2022, Benoit BLANCHON
+// Copyright © 2014-2023, Benoit BLANCHON
 // MIT License
 
 #include <ArduinoJson.h>
diff --git a/neosensor/libraries/ArduinoJson/extras/tests/JsonDocument/createNested.cpp b/neosensor/libraries/ArduinoJson/extras/tests/JsonDocument/createNested.cpp
index 258f0517..0c943575 100644
--- a/neosensor/libraries/ArduinoJson/extras/tests/JsonDocument/createNested.cpp
+++ b/neosensor/libraries/ArduinoJson/extras/tests/JsonDocument/createNested.cpp
@@ -1,5 +1,5 @@
 // ArduinoJson - https://arduinojson.org
-// Copyright © 2014-2022, Benoit BLANCHON
+// Copyright © 2014-2023, Benoit BLANCHON
 // MIT License
 
 #include <ArduinoJson.h>
diff --git a/neosensor/libraries/ArduinoJson/extras/tests/JsonDocument/isNull.cpp b/neosensor/libraries/ArduinoJson/extras/tests/JsonDocument/isNull.cpp
index cfb70dd6..d5aaae02 100644
--- a/neosensor/libraries/ArduinoJson/extras/tests/JsonDocument/isNull.cpp
+++ b/neosensor/libraries/ArduinoJson/extras/tests/JsonDocument/isNull.cpp
@@ -1,5 +1,5 @@
 // ArduinoJson - https://arduinojson.org
-// Copyright © 2014-2022, Benoit BLANCHON
+// Copyright © 2014-2023, Benoit BLANCHON
 // MIT License
 
 #include <ArduinoJson.h>
diff --git a/neosensor/libraries/ArduinoJson/extras/tests/Cpp11/issue1120.cpp b/neosensor/libraries/ArduinoJson/extras/tests/JsonDocument/issue1120.cpp
similarity index 100%
rename from neosensor/libraries/ArduinoJson/extras/tests/Cpp11/issue1120.cpp
rename to neosensor/libraries/ArduinoJson/extras/tests/JsonDocument/issue1120.cpp
diff --git a/neosensor/libraries/ArduinoJson/extras/tests/JsonDocument/nesting.cpp b/neosensor/libraries/ArduinoJson/extras/tests/JsonDocument/nesting.cpp
index 3284dd1c..7ac7e768 100644
--- a/neosensor/libraries/ArduinoJson/extras/tests/JsonDocument/nesting.cpp
+++ b/neosensor/libraries/ArduinoJson/extras/tests/JsonDocument/nesting.cpp
@@ -1,5 +1,5 @@
 // ArduinoJson - https://arduinojson.org
-// Copyright © 2014-2022, Benoit BLANCHON
+// Copyright © 2014-2023, Benoit BLANCHON
 // MIT License
 
 #include <ArduinoJson.h>
diff --git a/neosensor/libraries/ArduinoJson/extras/tests/JsonDocument/overflowed.cpp b/neosensor/libraries/ArduinoJson/extras/tests/JsonDocument/overflowed.cpp
index cea0db9d..e46d21b0 100644
--- a/neosensor/libraries/ArduinoJson/extras/tests/JsonDocument/overflowed.cpp
+++ b/neosensor/libraries/ArduinoJson/extras/tests/JsonDocument/overflowed.cpp
@@ -1,5 +1,5 @@
 // ArduinoJson - https://arduinojson.org
-// Copyright © 2014-2022, Benoit BLANCHON
+// Copyright © 2014-2023, Benoit BLANCHON
 // MIT License
 
 #include <ArduinoJson.h>
diff --git a/neosensor/libraries/ArduinoJson/extras/tests/JsonDocument/remove.cpp b/neosensor/libraries/ArduinoJson/extras/tests/JsonDocument/remove.cpp
index b7770733..724b324d 100644
--- a/neosensor/libraries/ArduinoJson/extras/tests/JsonDocument/remove.cpp
+++ b/neosensor/libraries/ArduinoJson/extras/tests/JsonDocument/remove.cpp
@@ -1,5 +1,5 @@
 // ArduinoJson - https://arduinojson.org
-// Copyright © 2014-2022, Benoit BLANCHON
+// Copyright © 2014-2023, Benoit BLANCHON
 // MIT License
 
 #include <ArduinoJson.h>
diff --git a/neosensor/libraries/ArduinoJson/extras/tests/JsonDocument/shrinkToFit.cpp b/neosensor/libraries/ArduinoJson/extras/tests/JsonDocument/shrinkToFit.cpp
index 0eb34e81..a4423340 100644
--- a/neosensor/libraries/ArduinoJson/extras/tests/JsonDocument/shrinkToFit.cpp
+++ b/neosensor/libraries/ArduinoJson/extras/tests/JsonDocument/shrinkToFit.cpp
@@ -1,5 +1,5 @@
 // ArduinoJson - https://arduinojson.org
-// Copyright © 2014-2022, Benoit BLANCHON
+// Copyright © 2014-2023, Benoit BLANCHON
 // MIT License
 
 #include <ArduinoJson.h>
@@ -8,40 +8,38 @@
 #include <stdlib.h>  // malloc, free
 #include <string>
 
-using ARDUINOJSON_NAMESPACE::addPadding;
-
 class ArmoredAllocator {
  public:
-  ArmoredAllocator() : _ptr(0), _size(0) {}
+  ArmoredAllocator() : ptr_(0), size_(0) {}
 
   void* allocate(size_t size) {
-    _ptr = malloc(size);
-    _size = size;
-    return _ptr;
+    ptr_ = malloc(size);
+    size_ = size;
+    return ptr_;
   }
 
   void deallocate(void* ptr) {
-    REQUIRE(ptr == _ptr);
+    REQUIRE(ptr == ptr_);
     free(ptr);
-    _ptr = 0;
-    _size = 0;
+    ptr_ = 0;
+    size_ = 0;
   }
 
   void* reallocate(void* ptr, size_t new_size) {
-    REQUIRE(ptr == _ptr);
+    REQUIRE(ptr == ptr_);
     // don't call realloc, instead alloc a new buffer and erase the old one
     // this way we make sure we support relocation
     void* new_ptr = malloc(new_size);
-    memcpy(new_ptr, _ptr, std::min(new_size, _size));
-    memset(_ptr, '#', _size);  // erase
-    free(_ptr);
-    _ptr = new_ptr;
+    memcpy(new_ptr, ptr_, std::min(new_size, size_));
+    memset(ptr_, '#', size_);  // erase
+    free(ptr_);
+    ptr_ = new_ptr;
     return new_ptr;
   }
 
  private:
-  void* _ptr;
-  size_t _size;
+  void* ptr_;
+  size_t size_;
 };
 
 typedef BasicJsonDocument<ArmoredAllocator> ShrinkToFitTestDocument;
diff --git a/neosensor/libraries/ArduinoJson/extras/tests/JsonDocument/size.cpp b/neosensor/libraries/ArduinoJson/extras/tests/JsonDocument/size.cpp
index 4fbb6e23..b3205a14 100644
--- a/neosensor/libraries/ArduinoJson/extras/tests/JsonDocument/size.cpp
+++ b/neosensor/libraries/ArduinoJson/extras/tests/JsonDocument/size.cpp
@@ -1,5 +1,5 @@
 // ArduinoJson - https://arduinojson.org
-// Copyright © 2014-2022, Benoit BLANCHON
+// Copyright © 2014-2023, Benoit BLANCHON
 // MIT License
 
 #include <ArduinoJson.h>
diff --git a/neosensor/libraries/ArduinoJson/extras/tests/JsonDocument/subscript.cpp b/neosensor/libraries/ArduinoJson/extras/tests/JsonDocument/subscript.cpp
index 4ba6ff84..f4186f85 100644
--- a/neosensor/libraries/ArduinoJson/extras/tests/JsonDocument/subscript.cpp
+++ b/neosensor/libraries/ArduinoJson/extras/tests/JsonDocument/subscript.cpp
@@ -1,5 +1,5 @@
 // ArduinoJson - https://arduinojson.org
-// Copyright © 2014-2022, Benoit BLANCHON
+// Copyright © 2014-2023, Benoit BLANCHON
 // MIT License
 
 #include <ArduinoJson.h>
diff --git a/neosensor/libraries/ArduinoJson/extras/tests/JsonObject/CMakeLists.txt b/neosensor/libraries/ArduinoJson/extras/tests/JsonObject/CMakeLists.txt
index 033e73ea..8676811c 100644
--- a/neosensor/libraries/ArduinoJson/extras/tests/JsonObject/CMakeLists.txt
+++ b/neosensor/libraries/ArduinoJson/extras/tests/JsonObject/CMakeLists.txt
@@ -1,5 +1,5 @@
 # ArduinoJson - https://arduinojson.org
-# Copyright © 2014-2022, Benoit BLANCHON
+# Copyright © 2014-2023, Benoit BLANCHON
 # MIT License
 
 add_executable(JsonObjectTests
@@ -25,5 +25,5 @@ add_test(JsonObject JsonObjectTests)
 
 set_tests_properties(JsonObject
 	PROPERTIES
-		LABELS 		"Catch"
+		LABELS "Catch"
 )
diff --git a/neosensor/libraries/ArduinoJson/extras/tests/JsonObject/clear.cpp b/neosensor/libraries/ArduinoJson/extras/tests/JsonObject/clear.cpp
index da17b2cf..e112a8dc 100644
--- a/neosensor/libraries/ArduinoJson/extras/tests/JsonObject/clear.cpp
+++ b/neosensor/libraries/ArduinoJson/extras/tests/JsonObject/clear.cpp
@@ -1,5 +1,5 @@
 // ArduinoJson - https://arduinojson.org
-// Copyright © 2014-2022, Benoit BLANCHON
+// Copyright © 2014-2023, Benoit BLANCHON
 // MIT License
 
 #include <ArduinoJson.h>
diff --git a/neosensor/libraries/ArduinoJson/extras/tests/JsonObject/compare.cpp b/neosensor/libraries/ArduinoJson/extras/tests/JsonObject/compare.cpp
index ffdb9aa2..9b747e3b 100644
--- a/neosensor/libraries/ArduinoJson/extras/tests/JsonObject/compare.cpp
+++ b/neosensor/libraries/ArduinoJson/extras/tests/JsonObject/compare.cpp
@@ -1,5 +1,5 @@
 // ArduinoJson - https://arduinojson.org
-// Copyright © 2014-2022, Benoit BLANCHON
+// Copyright © 2014-2023, Benoit BLANCHON
 // MIT License
 
 #include <ArduinoJson.h>
diff --git a/neosensor/libraries/ArduinoJson/extras/tests/JsonObject/containsKey.cpp b/neosensor/libraries/ArduinoJson/extras/tests/JsonObject/containsKey.cpp
index 4f37f7e6..85335a88 100644
--- a/neosensor/libraries/ArduinoJson/extras/tests/JsonObject/containsKey.cpp
+++ b/neosensor/libraries/ArduinoJson/extras/tests/JsonObject/containsKey.cpp
@@ -1,5 +1,5 @@
 // ArduinoJson - https://arduinojson.org
-// Copyright © 2014-2022, Benoit BLANCHON
+// Copyright © 2014-2023, Benoit BLANCHON
 // MIT License
 
 #include <ArduinoJson.h>
diff --git a/neosensor/libraries/ArduinoJson/extras/tests/JsonObject/copy.cpp b/neosensor/libraries/ArduinoJson/extras/tests/JsonObject/copy.cpp
index c5514a21..a4d5b5fa 100644
--- a/neosensor/libraries/ArduinoJson/extras/tests/JsonObject/copy.cpp
+++ b/neosensor/libraries/ArduinoJson/extras/tests/JsonObject/copy.cpp
@@ -1,5 +1,5 @@
 // ArduinoJson - https://arduinojson.org
-// Copyright © 2014-2022, Benoit BLANCHON
+// Copyright © 2014-2023, Benoit BLANCHON
 // MIT License
 
 #include <ArduinoJson.h>
diff --git a/neosensor/libraries/ArduinoJson/extras/tests/JsonObject/createNestedArray.cpp b/neosensor/libraries/ArduinoJson/extras/tests/JsonObject/createNestedArray.cpp
index 4d43038f..26c1a860 100644
--- a/neosensor/libraries/ArduinoJson/extras/tests/JsonObject/createNestedArray.cpp
+++ b/neosensor/libraries/ArduinoJson/extras/tests/JsonObject/createNestedArray.cpp
@@ -1,5 +1,5 @@
 // ArduinoJson - https://arduinojson.org
-// Copyright © 2014-2022, Benoit BLANCHON
+// Copyright © 2014-2023, Benoit BLANCHON
 // MIT License
 
 #include <ArduinoJson.h>
diff --git a/neosensor/libraries/ArduinoJson/extras/tests/JsonObject/createNestedObject.cpp b/neosensor/libraries/ArduinoJson/extras/tests/JsonObject/createNestedObject.cpp
index 29f4d39d..9ea067a7 100644
--- a/neosensor/libraries/ArduinoJson/extras/tests/JsonObject/createNestedObject.cpp
+++ b/neosensor/libraries/ArduinoJson/extras/tests/JsonObject/createNestedObject.cpp
@@ -1,5 +1,5 @@
 // ArduinoJson - https://arduinojson.org
-// Copyright © 2014-2022, Benoit BLANCHON
+// Copyright © 2014-2023, Benoit BLANCHON
 // MIT License
 
 #include <ArduinoJson.h>
diff --git a/neosensor/libraries/ArduinoJson/extras/tests/JsonObject/equals.cpp b/neosensor/libraries/ArduinoJson/extras/tests/JsonObject/equals.cpp
index 101c6126..02d451f9 100644
--- a/neosensor/libraries/ArduinoJson/extras/tests/JsonObject/equals.cpp
+++ b/neosensor/libraries/ArduinoJson/extras/tests/JsonObject/equals.cpp
@@ -1,5 +1,5 @@
 // ArduinoJson - https://arduinojson.org
-// Copyright © 2014-2022, Benoit BLANCHON
+// Copyright © 2014-2023, Benoit BLANCHON
 // MIT License
 
 #include <ArduinoJson.h>
diff --git a/neosensor/libraries/ArduinoJson/extras/tests/JsonObject/invalid.cpp b/neosensor/libraries/ArduinoJson/extras/tests/JsonObject/invalid.cpp
index 199c8f03..0695b481 100644
--- a/neosensor/libraries/ArduinoJson/extras/tests/JsonObject/invalid.cpp
+++ b/neosensor/libraries/ArduinoJson/extras/tests/JsonObject/invalid.cpp
@@ -1,5 +1,5 @@
 // ArduinoJson - https://arduinojson.org
-// Copyright © 2014-2022, Benoit BLANCHON
+// Copyright © 2014-2023, Benoit BLANCHON
 // MIT License
 
 #include <ArduinoJson.h>
diff --git a/neosensor/libraries/ArduinoJson/extras/tests/JsonObject/isNull.cpp b/neosensor/libraries/ArduinoJson/extras/tests/JsonObject/isNull.cpp
index 44403481..9fdff955 100644
--- a/neosensor/libraries/ArduinoJson/extras/tests/JsonObject/isNull.cpp
+++ b/neosensor/libraries/ArduinoJson/extras/tests/JsonObject/isNull.cpp
@@ -1,5 +1,5 @@
 // ArduinoJson - https://arduinojson.org
-// Copyright © 2014-2022, Benoit BLANCHON
+// Copyright © 2014-2023, Benoit BLANCHON
 // MIT License
 
 #include <ArduinoJson.h>
diff --git a/neosensor/libraries/ArduinoJson/extras/tests/JsonObject/iterator.cpp b/neosensor/libraries/ArduinoJson/extras/tests/JsonObject/iterator.cpp
index e9f2cd29..c48de5f6 100644
--- a/neosensor/libraries/ArduinoJson/extras/tests/JsonObject/iterator.cpp
+++ b/neosensor/libraries/ArduinoJson/extras/tests/JsonObject/iterator.cpp
@@ -1,5 +1,5 @@
 // ArduinoJson - https://arduinojson.org
-// Copyright © 2014-2022, Benoit BLANCHON
+// Copyright © 2014-2023, Benoit BLANCHON
 // MIT License
 
 #include <ArduinoJson.h>
diff --git a/neosensor/libraries/ArduinoJson/extras/tests/JsonObject/memoryUsage.cpp b/neosensor/libraries/ArduinoJson/extras/tests/JsonObject/memoryUsage.cpp
index 835ee4a7..406bcda4 100644
--- a/neosensor/libraries/ArduinoJson/extras/tests/JsonObject/memoryUsage.cpp
+++ b/neosensor/libraries/ArduinoJson/extras/tests/JsonObject/memoryUsage.cpp
@@ -1,5 +1,5 @@
 // ArduinoJson - https://arduinojson.org
-// Copyright © 2014-2022, Benoit BLANCHON
+// Copyright © 2014-2023, Benoit BLANCHON
 // MIT License
 
 #include <ArduinoJson.h>
diff --git a/neosensor/libraries/ArduinoJson/extras/tests/JsonObject/nesting.cpp b/neosensor/libraries/ArduinoJson/extras/tests/JsonObject/nesting.cpp
index 7937c289..2ba98adf 100644
--- a/neosensor/libraries/ArduinoJson/extras/tests/JsonObject/nesting.cpp
+++ b/neosensor/libraries/ArduinoJson/extras/tests/JsonObject/nesting.cpp
@@ -1,5 +1,5 @@
 // ArduinoJson - https://arduinojson.org
-// Copyright © 2014-2022, Benoit BLANCHON
+// Copyright © 2014-2023, Benoit BLANCHON
 // MIT License
 
 #include <ArduinoJson.h>
diff --git a/neosensor/libraries/ArduinoJson/extras/tests/JsonObject/remove.cpp b/neosensor/libraries/ArduinoJson/extras/tests/JsonObject/remove.cpp
index f1cb20cb..1dbd4bd5 100644
--- a/neosensor/libraries/ArduinoJson/extras/tests/JsonObject/remove.cpp
+++ b/neosensor/libraries/ArduinoJson/extras/tests/JsonObject/remove.cpp
@@ -1,5 +1,5 @@
 // ArduinoJson - https://arduinojson.org
-// Copyright © 2014-2022, Benoit BLANCHON
+// Copyright © 2014-2023, Benoit BLANCHON
 // MIT License
 
 #include <ArduinoJson.h>
diff --git a/neosensor/libraries/ArduinoJson/extras/tests/JsonObject/size.cpp b/neosensor/libraries/ArduinoJson/extras/tests/JsonObject/size.cpp
index 0d82cfb8..d9c7bb78 100644
--- a/neosensor/libraries/ArduinoJson/extras/tests/JsonObject/size.cpp
+++ b/neosensor/libraries/ArduinoJson/extras/tests/JsonObject/size.cpp
@@ -1,5 +1,5 @@
 // ArduinoJson - https://arduinojson.org
-// Copyright © 2014-2022, Benoit BLANCHON
+// Copyright © 2014-2023, Benoit BLANCHON
 // MIT License
 
 #include <ArduinoJson.h>
diff --git a/neosensor/libraries/ArduinoJson/extras/tests/JsonObject/std_string.cpp b/neosensor/libraries/ArduinoJson/extras/tests/JsonObject/std_string.cpp
index bc74d014..8a93d3f0 100644
--- a/neosensor/libraries/ArduinoJson/extras/tests/JsonObject/std_string.cpp
+++ b/neosensor/libraries/ArduinoJson/extras/tests/JsonObject/std_string.cpp
@@ -1,5 +1,5 @@
 // ArduinoJson - https://arduinojson.org
-// Copyright © 2014-2022, Benoit BLANCHON
+// Copyright © 2014-2023, Benoit BLANCHON
 // MIT License
 
 #include <ArduinoJson.h>
diff --git a/neosensor/libraries/ArduinoJson/extras/tests/JsonObject/subscript.cpp b/neosensor/libraries/ArduinoJson/extras/tests/JsonObject/subscript.cpp
index e8242f63..afc567a6 100644
--- a/neosensor/libraries/ArduinoJson/extras/tests/JsonObject/subscript.cpp
+++ b/neosensor/libraries/ArduinoJson/extras/tests/JsonObject/subscript.cpp
@@ -1,5 +1,5 @@
 // ArduinoJson - https://arduinojson.org
-// Copyright © 2014-2022, Benoit BLANCHON
+// Copyright © 2014-2023, Benoit BLANCHON
 // MIT License
 
 #include <ArduinoJson.h>
diff --git a/neosensor/libraries/ArduinoJson/extras/tests/JsonSerializer/CMakeLists.txt b/neosensor/libraries/ArduinoJson/extras/tests/JsonSerializer/CMakeLists.txt
index ece6dbc2..0ec4937f 100644
--- a/neosensor/libraries/ArduinoJson/extras/tests/JsonSerializer/CMakeLists.txt
+++ b/neosensor/libraries/ArduinoJson/extras/tests/JsonSerializer/CMakeLists.txt
@@ -1,5 +1,5 @@
 # ArduinoJson - https://arduinojson.org
-# Copyright © 2014-2022, Benoit BLANCHON
+# Copyright © 2014-2023, Benoit BLANCHON
 # MIT License
 
 add_executable(JsonSerializerTests
@@ -18,5 +18,5 @@ add_test(JsonSerializer JsonSerializerTests)
 
 set_tests_properties(JsonSerializer
 	PROPERTIES
-		LABELS 		"Catch"
+		LABELS "Catch"
 )
diff --git a/neosensor/libraries/ArduinoJson/extras/tests/JsonSerializer/CustomWriter.cpp b/neosensor/libraries/ArduinoJson/extras/tests/JsonSerializer/CustomWriter.cpp
index d4ba459c..a84657a7 100644
--- a/neosensor/libraries/ArduinoJson/extras/tests/JsonSerializer/CustomWriter.cpp
+++ b/neosensor/libraries/ArduinoJson/extras/tests/JsonSerializer/CustomWriter.cpp
@@ -1,5 +1,5 @@
 // ArduinoJson - https://arduinojson.org
-// Copyright © 2014-2022, Benoit BLANCHON
+// Copyright © 2014-2023, Benoit BLANCHON
 // MIT License
 
 #include <ArduinoJson.h>
@@ -8,26 +8,25 @@
 class CustomWriter {
  public:
   CustomWriter() {}
+  CustomWriter(const CustomWriter&) = delete;
+  CustomWriter& operator=(const CustomWriter&) = delete;
 
   size_t write(uint8_t c) {
-    _str.append(1, static_cast<char>(c));
+    str_.append(1, static_cast<char>(c));
     return 1;
   }
 
   size_t write(const uint8_t* s, size_t n) {
-    _str.append(reinterpret_cast<const char*>(s), n);
+    str_.append(reinterpret_cast<const char*>(s), n);
     return n;
   }
 
   const std::string& str() const {
-    return _str;
+    return str_;
   }
 
  private:
-  CustomWriter(const CustomWriter&);  // non-copiable
-  CustomWriter& operator=(const CustomWriter&);
-
-  std::string _str;
+  std::string str_;
 };
 
 TEST_CASE("CustomWriter") {
diff --git a/neosensor/libraries/ArduinoJson/extras/tests/JsonSerializer/JsonArray.cpp b/neosensor/libraries/ArduinoJson/extras/tests/JsonSerializer/JsonArray.cpp
index 7cecbf4e..1fad69dd 100644
--- a/neosensor/libraries/ArduinoJson/extras/tests/JsonSerializer/JsonArray.cpp
+++ b/neosensor/libraries/ArduinoJson/extras/tests/JsonSerializer/JsonArray.cpp
@@ -1,5 +1,5 @@
 // ArduinoJson - https://arduinojson.org
-// Copyright © 2014-2022, Benoit BLANCHON
+// Copyright © 2014-2023, Benoit BLANCHON
 // MIT License
 
 #include <ArduinoJson.h>
diff --git a/neosensor/libraries/ArduinoJson/extras/tests/JsonSerializer/JsonArrayPretty.cpp b/neosensor/libraries/ArduinoJson/extras/tests/JsonSerializer/JsonArrayPretty.cpp
index 8c319fc0..9abeb015 100644
--- a/neosensor/libraries/ArduinoJson/extras/tests/JsonSerializer/JsonArrayPretty.cpp
+++ b/neosensor/libraries/ArduinoJson/extras/tests/JsonSerializer/JsonArrayPretty.cpp
@@ -1,5 +1,5 @@
 // ArduinoJson - https://arduinojson.org
-// Copyright © 2014-2022, Benoit BLANCHON
+// Copyright © 2014-2023, Benoit BLANCHON
 // MIT License
 
 #include <ArduinoJson.h>
diff --git a/neosensor/libraries/ArduinoJson/extras/tests/JsonSerializer/JsonObject.cpp b/neosensor/libraries/ArduinoJson/extras/tests/JsonSerializer/JsonObject.cpp
index 20b90f52..9ba21141 100644
--- a/neosensor/libraries/ArduinoJson/extras/tests/JsonSerializer/JsonObject.cpp
+++ b/neosensor/libraries/ArduinoJson/extras/tests/JsonSerializer/JsonObject.cpp
@@ -1,5 +1,5 @@
 // ArduinoJson - https://arduinojson.org
-// Copyright © 2014-2022, Benoit BLANCHON
+// Copyright © 2014-2023, Benoit BLANCHON
 // MIT License
 
 #include <ArduinoJson.h>
diff --git a/neosensor/libraries/ArduinoJson/extras/tests/JsonSerializer/JsonObjectPretty.cpp b/neosensor/libraries/ArduinoJson/extras/tests/JsonSerializer/JsonObjectPretty.cpp
index ec4ae71b..4e8d113e 100644
--- a/neosensor/libraries/ArduinoJson/extras/tests/JsonSerializer/JsonObjectPretty.cpp
+++ b/neosensor/libraries/ArduinoJson/extras/tests/JsonSerializer/JsonObjectPretty.cpp
@@ -1,5 +1,5 @@
 // ArduinoJson - https://arduinojson.org
-// Copyright © 2014-2022, Benoit BLANCHON
+// Copyright © 2014-2023, Benoit BLANCHON
 // MIT License
 
 #include <ArduinoJson.h>
diff --git a/neosensor/libraries/ArduinoJson/extras/tests/JsonSerializer/JsonVariant.cpp b/neosensor/libraries/ArduinoJson/extras/tests/JsonSerializer/JsonVariant.cpp
index 3525a1a4..81553d31 100644
--- a/neosensor/libraries/ArduinoJson/extras/tests/JsonSerializer/JsonVariant.cpp
+++ b/neosensor/libraries/ArduinoJson/extras/tests/JsonSerializer/JsonVariant.cpp
@@ -1,5 +1,5 @@
 // ArduinoJson - https://arduinojson.org
-// Copyright © 2014-2022, Benoit BLANCHON
+// Copyright © 2014-2023, Benoit BLANCHON
 // MIT License
 
 #include <ArduinoJson.h>
diff --git a/neosensor/libraries/ArduinoJson/extras/tests/JsonSerializer/std_stream.cpp b/neosensor/libraries/ArduinoJson/extras/tests/JsonSerializer/std_stream.cpp
index 74520286..411f4792 100644
--- a/neosensor/libraries/ArduinoJson/extras/tests/JsonSerializer/std_stream.cpp
+++ b/neosensor/libraries/ArduinoJson/extras/tests/JsonSerializer/std_stream.cpp
@@ -1,5 +1,5 @@
 // ArduinoJson - https://arduinojson.org
-// Copyright © 2014-2022, Benoit BLANCHON
+// Copyright © 2014-2023, Benoit BLANCHON
 // MIT License
 
 #include <ArduinoJson.h>
diff --git a/neosensor/libraries/ArduinoJson/extras/tests/JsonSerializer/std_string.cpp b/neosensor/libraries/ArduinoJson/extras/tests/JsonSerializer/std_string.cpp
index 7a145da8..af0d8096 100644
--- a/neosensor/libraries/ArduinoJson/extras/tests/JsonSerializer/std_string.cpp
+++ b/neosensor/libraries/ArduinoJson/extras/tests/JsonSerializer/std_string.cpp
@@ -1,5 +1,5 @@
 // ArduinoJson - https://arduinojson.org
-// Copyright © 2014-2022, Benoit BLANCHON
+// Copyright © 2014-2023, Benoit BLANCHON
 // MIT License
 
 #include <ArduinoJson.h>
diff --git a/neosensor/libraries/ArduinoJson/extras/tests/JsonVariant/CMakeLists.txt b/neosensor/libraries/ArduinoJson/extras/tests/JsonVariant/CMakeLists.txt
index 1a0d14a9..d43a971c 100644
--- a/neosensor/libraries/ArduinoJson/extras/tests/JsonVariant/CMakeLists.txt
+++ b/neosensor/libraries/ArduinoJson/extras/tests/JsonVariant/CMakeLists.txt
@@ -1,5 +1,5 @@
 # ArduinoJson - https://arduinojson.org
-# Copyright © 2014-2022, Benoit BLANCHON
+# Copyright © 2014-2023, Benoit BLANCHON
 # MIT License
 
 add_executable(JsonVariantTests
@@ -16,12 +16,14 @@ add_executable(JsonVariantTests
 	memoryUsage.cpp
 	misc.cpp
 	nesting.cpp
+	nullptr.cpp
 	or.cpp
 	overflow.cpp
 	remove.cpp
 	set.cpp
 	shallowCopy.cpp
 	size.cpp
+	stl_containers.cpp
 	subscript.cpp
 	types.cpp
 	unbound.cpp
@@ -31,5 +33,5 @@ add_test(JsonVariant JsonVariantTests)
 
 set_tests_properties(JsonVariant
 	PROPERTIES
-		LABELS 		"Catch"
+		LABELS "Catch"
 )
diff --git a/neosensor/libraries/ArduinoJson/extras/tests/JsonVariant/add.cpp b/neosensor/libraries/ArduinoJson/extras/tests/JsonVariant/add.cpp
index 526e914d..6907ae3e 100644
--- a/neosensor/libraries/ArduinoJson/extras/tests/JsonVariant/add.cpp
+++ b/neosensor/libraries/ArduinoJson/extras/tests/JsonVariant/add.cpp
@@ -1,5 +1,5 @@
 // ArduinoJson - https://arduinojson.org
-// Copyright © 2014-2022, Benoit BLANCHON
+// Copyright © 2014-2023, Benoit BLANCHON
 // MIT License
 
 #include <ArduinoJson.h>
diff --git a/neosensor/libraries/ArduinoJson/extras/tests/JsonVariant/as.cpp b/neosensor/libraries/ArduinoJson/extras/tests/JsonVariant/as.cpp
index c9956778..01e263e9 100644
--- a/neosensor/libraries/ArduinoJson/extras/tests/JsonVariant/as.cpp
+++ b/neosensor/libraries/ArduinoJson/extras/tests/JsonVariant/as.cpp
@@ -1,5 +1,5 @@
 // ArduinoJson - https://arduinojson.org
-// Copyright © 2014-2022, Benoit BLANCHON
+// Copyright © 2014-2023, Benoit BLANCHON
 // MIT License
 
 #include <ArduinoJson.h>
@@ -7,7 +7,7 @@
 #include <catch.hpp>
 
 namespace my {
-using ARDUINOJSON_NAMESPACE::isinf;
+using ArduinoJson::detail::isinf;
 }  // namespace my
 
 enum MY_ENUM { ONE = 1, TWO = 2 };
diff --git a/neosensor/libraries/ArduinoJson/extras/tests/JsonVariant/clear.cpp b/neosensor/libraries/ArduinoJson/extras/tests/JsonVariant/clear.cpp
index 2b40e324..d516ca6c 100644
--- a/neosensor/libraries/ArduinoJson/extras/tests/JsonVariant/clear.cpp
+++ b/neosensor/libraries/ArduinoJson/extras/tests/JsonVariant/clear.cpp
@@ -1,5 +1,5 @@
 // ArduinoJson - https://arduinojson.org
-// Copyright © 2014-2022, Benoit BLANCHON
+// Copyright © 2014-2023, Benoit BLANCHON
 // MIT License
 
 #include <ArduinoJson.h>
diff --git a/neosensor/libraries/ArduinoJson/extras/tests/JsonVariant/compare.cpp b/neosensor/libraries/ArduinoJson/extras/tests/JsonVariant/compare.cpp
index 2cbdaa3d..1922df03 100644
--- a/neosensor/libraries/ArduinoJson/extras/tests/JsonVariant/compare.cpp
+++ b/neosensor/libraries/ArduinoJson/extras/tests/JsonVariant/compare.cpp
@@ -1,5 +1,5 @@
 // ArduinoJson - https://arduinojson.org
-// Copyright © 2014-2022, Benoit BLANCHON
+// Copyright © 2014-2023, Benoit BLANCHON
 // MIT License
 
 #include <ArduinoJson.h>
diff --git a/neosensor/libraries/ArduinoJson/extras/tests/JsonVariant/containsKey.cpp b/neosensor/libraries/ArduinoJson/extras/tests/JsonVariant/containsKey.cpp
index fca684ee..d003d11d 100644
--- a/neosensor/libraries/ArduinoJson/extras/tests/JsonVariant/containsKey.cpp
+++ b/neosensor/libraries/ArduinoJson/extras/tests/JsonVariant/containsKey.cpp
@@ -1,13 +1,11 @@
 // ArduinoJson - https://arduinojson.org
-// Copyright © 2014-2022, Benoit BLANCHON
+// Copyright © 2014-2023, Benoit BLANCHON
 // MIT License
 
 #include <ArduinoJson.h>
 #include <stdint.h>
 #include <catch.hpp>
 
-static const char* null = 0;
-
 TEST_CASE("JsonVariant::containsKey()") {
   DynamicJsonDocument doc(4096);
   JsonVariant var = doc.to<JsonVariant>();
diff --git a/neosensor/libraries/ArduinoJson/extras/tests/JsonVariant/converters.cpp b/neosensor/libraries/ArduinoJson/extras/tests/JsonVariant/converters.cpp
index 7281f2ba..89b34ded 100644
--- a/neosensor/libraries/ArduinoJson/extras/tests/JsonVariant/converters.cpp
+++ b/neosensor/libraries/ArduinoJson/extras/tests/JsonVariant/converters.cpp
@@ -1,5 +1,5 @@
 // ArduinoJson - https://arduinojson.org
-// Copyright © 2014-2022, Benoit BLANCHON
+// Copyright © 2014-2023, Benoit BLANCHON
 // MIT License
 
 #include <ArduinoJson.h>
@@ -74,21 +74,21 @@ TEST_CASE("Custom converter with overloading") {
 
 class Complex {
  public:
-  explicit Complex(double r, double i) : _real(r), _imag(i) {}
+  explicit Complex(double r, double i) : real_(r), imag_(i) {}
 
   double real() const {
-    return _real;
+    return real_;
   }
 
   double imag() const {
-    return _imag;
+    return imag_;
   }
 
  private:
-  double _real, _imag;
+  double real_, imag_;
 };
 
-namespace ARDUINOJSON_NAMESPACE {
+namespace ArduinoJson {
 template <>
 struct Converter<Complex> {
   static void toJson(const Complex& src, JsonVariant dst) {
@@ -104,7 +104,7 @@ struct Converter<Complex> {
     return src["real"].is<double>() && src["imag"].is<double>();
   }
 };
-}  // namespace ARDUINOJSON_NAMESPACE
+}  // namespace ArduinoJson
 
 TEST_CASE("Custom converter with specialization") {
   DynamicJsonDocument doc(4096);
@@ -142,7 +142,7 @@ TEST_CASE("Custom converter with specialization") {
 }
 
 TEST_CASE("ConverterNeedsWriteableRef") {
-  using namespace ARDUINOJSON_NAMESPACE;
+  using namespace ArduinoJson::detail;
   CHECK(ConverterNeedsWriteableRef<int>::value == false);
   CHECK(ConverterNeedsWriteableRef<float>::value == false);
   CHECK(ConverterNeedsWriteableRef<JsonVariant>::value == true);
diff --git a/neosensor/libraries/ArduinoJson/extras/tests/JsonVariant/copy.cpp b/neosensor/libraries/ArduinoJson/extras/tests/JsonVariant/copy.cpp
index 7784f0ca..0d96bf9f 100644
--- a/neosensor/libraries/ArduinoJson/extras/tests/JsonVariant/copy.cpp
+++ b/neosensor/libraries/ArduinoJson/extras/tests/JsonVariant/copy.cpp
@@ -1,5 +1,5 @@
 // ArduinoJson - https://arduinojson.org
-// Copyright © 2014-2022, Benoit BLANCHON
+// Copyright © 2014-2023, Benoit BLANCHON
 // MIT License
 
 #include <ArduinoJson.h>
diff --git a/neosensor/libraries/ArduinoJson/extras/tests/JsonVariant/createNested.cpp b/neosensor/libraries/ArduinoJson/extras/tests/JsonVariant/createNested.cpp
index efd75015..e3958231 100644
--- a/neosensor/libraries/ArduinoJson/extras/tests/JsonVariant/createNested.cpp
+++ b/neosensor/libraries/ArduinoJson/extras/tests/JsonVariant/createNested.cpp
@@ -1,5 +1,5 @@
 // ArduinoJson - https://arduinojson.org
-// Copyright © 2014-2022, Benoit BLANCHON
+// Copyright © 2014-2023, Benoit BLANCHON
 // MIT License
 
 #include <ArduinoJson.h>
diff --git a/neosensor/libraries/ArduinoJson/extras/tests/JsonVariant/is.cpp b/neosensor/libraries/ArduinoJson/extras/tests/JsonVariant/is.cpp
index 479e923a..6a414a42 100644
--- a/neosensor/libraries/ArduinoJson/extras/tests/JsonVariant/is.cpp
+++ b/neosensor/libraries/ArduinoJson/extras/tests/JsonVariant/is.cpp
@@ -1,5 +1,5 @@
 // ArduinoJson - https://arduinojson.org
-// Copyright © 2014-2022, Benoit BLANCHON
+// Copyright © 2014-2023, Benoit BLANCHON
 // MIT License
 
 #include <ArduinoJson.h>
diff --git a/neosensor/libraries/ArduinoJson/extras/tests/JsonVariant/isnull.cpp b/neosensor/libraries/ArduinoJson/extras/tests/JsonVariant/isnull.cpp
index 1ec6f353..bb9de714 100644
--- a/neosensor/libraries/ArduinoJson/extras/tests/JsonVariant/isnull.cpp
+++ b/neosensor/libraries/ArduinoJson/extras/tests/JsonVariant/isnull.cpp
@@ -1,5 +1,5 @@
 // ArduinoJson - https://arduinojson.org
-// Copyright © 2014-2022, Benoit BLANCHON
+// Copyright © 2014-2023, Benoit BLANCHON
 // MIT License
 
 #include <ArduinoJson.h>
diff --git a/neosensor/libraries/ArduinoJson/extras/tests/JsonVariant/memoryUsage.cpp b/neosensor/libraries/ArduinoJson/extras/tests/JsonVariant/memoryUsage.cpp
index 59587dbb..808acb3a 100644
--- a/neosensor/libraries/ArduinoJson/extras/tests/JsonVariant/memoryUsage.cpp
+++ b/neosensor/libraries/ArduinoJson/extras/tests/JsonVariant/memoryUsage.cpp
@@ -1,5 +1,5 @@
 // ArduinoJson - https://arduinojson.org
-// Copyright © 2014-2022, Benoit BLANCHON
+// Copyright © 2014-2023, Benoit BLANCHON
 // MIT License
 
 #include <ArduinoJson.h>
diff --git a/neosensor/libraries/ArduinoJson/extras/tests/JsonVariant/misc.cpp b/neosensor/libraries/ArduinoJson/extras/tests/JsonVariant/misc.cpp
index 05976c37..7089f640 100644
--- a/neosensor/libraries/ArduinoJson/extras/tests/JsonVariant/misc.cpp
+++ b/neosensor/libraries/ArduinoJson/extras/tests/JsonVariant/misc.cpp
@@ -1,10 +1,15 @@
 // ArduinoJson - https://arduinojson.org
-// Copyright © 2014-2022, Benoit BLANCHON
+// Copyright © 2014-2023, Benoit BLANCHON
 // MIT License
 
 #include <ArduinoJson.h>
 #include <catch.hpp>
 
+TEST_CASE("VariantData") {
+  REQUIRE(std::is_standard_layout<ArduinoJson::detail::VariantData>::value ==
+          true);
+}
+
 TEST_CASE("JsonVariant from JsonArray") {
   SECTION("JsonArray is null") {
     JsonArray arr;
diff --git a/neosensor/libraries/ArduinoJson/extras/tests/JsonVariant/nesting.cpp b/neosensor/libraries/ArduinoJson/extras/tests/JsonVariant/nesting.cpp
index 3e936fbe..1028a283 100644
--- a/neosensor/libraries/ArduinoJson/extras/tests/JsonVariant/nesting.cpp
+++ b/neosensor/libraries/ArduinoJson/extras/tests/JsonVariant/nesting.cpp
@@ -1,5 +1,5 @@
 // ArduinoJson - https://arduinojson.org
-// Copyright © 2014-2022, Benoit BLANCHON
+// Copyright © 2014-2023, Benoit BLANCHON
 // MIT License
 
 #include <ArduinoJson.h>
diff --git a/neosensor/libraries/ArduinoJson/extras/tests/Cpp11/nullptr.cpp b/neosensor/libraries/ArduinoJson/extras/tests/JsonVariant/nullptr.cpp
similarity index 75%
rename from neosensor/libraries/ArduinoJson/extras/tests/Cpp11/nullptr.cpp
rename to neosensor/libraries/ArduinoJson/extras/tests/JsonVariant/nullptr.cpp
index 813b9cc2..e26d9306 100644
--- a/neosensor/libraries/ArduinoJson/extras/tests/Cpp11/nullptr.cpp
+++ b/neosensor/libraries/ArduinoJson/extras/tests/JsonVariant/nullptr.cpp
@@ -2,24 +2,20 @@
 
 #include <catch.hpp>
 
-#if !ARDUINOJSON_HAS_NULLPTR
-#  error ARDUINOJSON_HAS_NULLPTR must be set to 1
-#endif
-
 TEST_CASE("nullptr") {
   DynamicJsonDocument doc(4096);
   JsonVariant variant = doc.to<JsonVariant>();
 
   SECTION("JsonVariant == nullptr") {
-    REQUIRE((variant == nullptr));
-    REQUIRE_FALSE((variant != nullptr));
+    REQUIRE(variant == nullptr);
+    REQUIRE_FALSE(variant != nullptr);
   }
 
   SECTION("JsonVariant != nullptr") {
     variant.set(42);
 
-    REQUIRE_FALSE((variant == nullptr));
-    REQUIRE((variant != nullptr));
+    REQUIRE_FALSE(variant == nullptr);
+    REQUIRE(variant != nullptr);
   }
 
   SECTION("JsonVariant.set(nullptr)") {
diff --git a/neosensor/libraries/ArduinoJson/extras/tests/JsonVariant/or.cpp b/neosensor/libraries/ArduinoJson/extras/tests/JsonVariant/or.cpp
index 7d1c190b..01ec4701 100644
--- a/neosensor/libraries/ArduinoJson/extras/tests/JsonVariant/or.cpp
+++ b/neosensor/libraries/ArduinoJson/extras/tests/JsonVariant/or.cpp
@@ -1,5 +1,5 @@
 // ArduinoJson - https://arduinojson.org
-// Copyright © 2014-2022, Benoit BLANCHON
+// Copyright © 2014-2023, Benoit BLANCHON
 // MIT License
 
 #include <ArduinoJson.h>
diff --git a/neosensor/libraries/ArduinoJson/extras/tests/JsonVariant/overflow.cpp b/neosensor/libraries/ArduinoJson/extras/tests/JsonVariant/overflow.cpp
index ff0e3aa5..1123e98b 100644
--- a/neosensor/libraries/ArduinoJson/extras/tests/JsonVariant/overflow.cpp
+++ b/neosensor/libraries/ArduinoJson/extras/tests/JsonVariant/overflow.cpp
@@ -1,5 +1,5 @@
 // ArduinoJson - https://arduinojson.org
-// Copyright © 2014-2022, Benoit BLANCHON
+// Copyright © 2014-2023, Benoit BLANCHON
 // MIT License
 
 #include <ArduinoJson.h>
diff --git a/neosensor/libraries/ArduinoJson/extras/tests/JsonVariant/remove.cpp b/neosensor/libraries/ArduinoJson/extras/tests/JsonVariant/remove.cpp
index c744e19b..0835bf0e 100644
--- a/neosensor/libraries/ArduinoJson/extras/tests/JsonVariant/remove.cpp
+++ b/neosensor/libraries/ArduinoJson/extras/tests/JsonVariant/remove.cpp
@@ -1,13 +1,11 @@
 // ArduinoJson - https://arduinojson.org
-// Copyright © 2014-2022, Benoit BLANCHON
+// Copyright © 2014-2023, Benoit BLANCHON
 // MIT License
 
 #include <ArduinoJson.h>
 #include <stdint.h>
 #include <catch.hpp>
 
-static const char* null = 0;
-
 TEST_CASE("JsonVariant::remove()") {
   DynamicJsonDocument doc(4096);
   JsonVariant var = doc.to<JsonVariant>();
diff --git a/neosensor/libraries/ArduinoJson/extras/tests/JsonVariant/set.cpp b/neosensor/libraries/ArduinoJson/extras/tests/JsonVariant/set.cpp
index 9188c98a..4743afb9 100644
--- a/neosensor/libraries/ArduinoJson/extras/tests/JsonVariant/set.cpp
+++ b/neosensor/libraries/ArduinoJson/extras/tests/JsonVariant/set.cpp
@@ -1,5 +1,5 @@
 // ArduinoJson - https://arduinojson.org
-// Copyright © 2014-2022, Benoit BLANCHON
+// Copyright © 2014-2023, Benoit BLANCHON
 // MIT License
 
 #include <ArduinoJson.h>
diff --git a/neosensor/libraries/ArduinoJson/extras/tests/JsonVariant/shallowCopy.cpp b/neosensor/libraries/ArduinoJson/extras/tests/JsonVariant/shallowCopy.cpp
index c7c334b7..28aa38c6 100644
--- a/neosensor/libraries/ArduinoJson/extras/tests/JsonVariant/shallowCopy.cpp
+++ b/neosensor/libraries/ArduinoJson/extras/tests/JsonVariant/shallowCopy.cpp
@@ -1,5 +1,5 @@
 // ArduinoJson - https://arduinojson.org
-// Copyright © 2014-2022, Benoit BLANCHON
+// Copyright © 2014-2023, Benoit BLANCHON
 // MIT License
 
 #include <ArduinoJson.h>
diff --git a/neosensor/libraries/ArduinoJson/extras/tests/JsonVariant/size.cpp b/neosensor/libraries/ArduinoJson/extras/tests/JsonVariant/size.cpp
index 73d9ced8..3a4ec467 100644
--- a/neosensor/libraries/ArduinoJson/extras/tests/JsonVariant/size.cpp
+++ b/neosensor/libraries/ArduinoJson/extras/tests/JsonVariant/size.cpp
@@ -1,5 +1,5 @@
 // ArduinoJson - https://arduinojson.org
-// Copyright © 2014-2022, Benoit BLANCHON
+// Copyright © 2014-2023, Benoit BLANCHON
 // MIT License
 
 #include <ArduinoJson.h>
diff --git a/neosensor/libraries/ArduinoJson/extras/tests/JsonVariant/stl_containers.cpp b/neosensor/libraries/ArduinoJson/extras/tests/JsonVariant/stl_containers.cpp
new file mode 100644
index 00000000..d6b9b60b
--- /dev/null
+++ b/neosensor/libraries/ArduinoJson/extras/tests/JsonVariant/stl_containers.cpp
@@ -0,0 +1,138 @@
+// ArduinoJson - https://arduinojson.org
+// Copyright © 2014-2023, Benoit BLANCHON
+// MIT License
+
+#include <ArduinoJson.h>
+#include <stdint.h>
+#include <catch.hpp>
+
+#include <array>
+#include <string>
+#include <vector>
+
+namespace ArduinoJson {
+template <typename T>
+struct Converter<std::vector<T>> {
+  static void toJson(const std::vector<T>& src, JsonVariant dst) {
+    JsonArray array = dst.to<JsonArray>();
+    for (T item : src)
+      array.add(item);
+  }
+
+  static std::vector<T> fromJson(JsonVariantConst src) {
+    std::vector<T> dst;
+    for (T item : src.as<JsonArrayConst>())
+      dst.push_back(item);
+    return dst;
+  }
+
+  static bool checkJson(JsonVariantConst src) {
+    JsonArrayConst array = src;
+    bool result = array;
+    for (JsonVariantConst item : array)
+      result &= item.is<T>();
+    return result;
+  }
+};
+
+template <typename T, size_t N>
+struct Converter<std::array<T, N>> {
+  static void toJson(const std::array<T, N>& src, JsonVariant dst) {
+    JsonArray array = dst.to<JsonArray>();
+    for (T item : src)
+      array.add(item);
+  }
+
+  static std::array<T, N> fromJson(JsonVariantConst src) {
+    std::array<T, N> dst;
+    dst.fill(0);
+    size_t idx = 0;
+    for (T item : src.as<JsonArrayConst>())
+      dst[idx++] = item;
+    return dst;
+  }
+
+  static bool checkJson(JsonVariantConst src) {
+    JsonArrayConst array = src;
+    bool result = array;
+    size_t size = 0;
+    for (JsonVariantConst item : array) {
+      result &= item.is<T>();
+      size++;
+    }
+    return result && size == N;
+  }
+};
+}  // namespace ArduinoJson
+
+TEST_CASE("vector<int>") {
+  SECTION("toJson") {
+    std::vector<int> v = {1, 2};
+
+    StaticJsonDocument<128> doc;
+    doc.set(v);
+    REQUIRE(doc.as<std::string>() == "[1,2]");
+  }
+
+  SECTION("fromJson") {
+    StaticJsonDocument<128> doc;
+    doc.add(1);
+    doc.add(2);
+
+    auto v = doc.as<std::vector<int>>();
+    REQUIRE(v.size() == 2);
+    CHECK(v[0] == 1);
+    CHECK(v[1] == 2);
+  }
+
+  SECTION("checkJson") {
+    StaticJsonDocument<128> doc;
+    CHECK(doc.is<std::vector<int>>() == false);
+
+    doc.add(1);
+    doc.add(2);
+    CHECK(doc.is<std::vector<int>>() == true);
+
+    doc.add("foo");
+    CHECK(doc.is<std::vector<int>>() == false);
+  }
+}
+
+TEST_CASE("array<int, 2>") {
+  typedef std::array<int, 2> array_type;
+
+  SECTION("toJson") {
+    array_type v;
+    v[0] = 1;
+    v[1] = 2;
+
+    StaticJsonDocument<128> doc;
+    doc.set(v);
+    REQUIRE(doc.as<std::string>() == "[1,2]");
+  }
+
+  SECTION("fromJson") {
+    StaticJsonDocument<128> doc;
+    doc.add(1);
+    doc.add(2);
+
+    auto v = doc.as<array_type>();
+    REQUIRE(v.size() == 2);
+    CHECK(v[0] == 1);
+    CHECK(v[1] == 2);
+  }
+
+  SECTION("checkJson") {
+    StaticJsonDocument<128> doc;
+    CHECK(doc.is<array_type>() == false);
+
+    doc.add(1);
+    CHECK(doc.is<array_type>() == false);
+
+    doc.add(2);
+    CHECK(doc.is<array_type>() == true);
+
+    doc[0] = "foo";
+    CHECK(doc.is<array_type>() == false);
+  }
+}
diff --git a/neosensor/libraries/ArduinoJson/extras/tests/JsonVariant/subscript.cpp b/neosensor/libraries/ArduinoJson/extras/tests/JsonVariant/subscript.cpp
index f466cd27..93f759b8 100644
--- a/neosensor/libraries/ArduinoJson/extras/tests/JsonVariant/subscript.cpp
+++ b/neosensor/libraries/ArduinoJson/extras/tests/JsonVariant/subscript.cpp
@@ -1,5 +1,5 @@
 // ArduinoJson - https://arduinojson.org
-// Copyright © 2014-2022, Benoit BLANCHON
+// Copyright © 2014-2023, Benoit BLANCHON
 // MIT License
 
 #include <ArduinoJson.h>
diff --git a/neosensor/libraries/ArduinoJson/extras/tests/JsonVariant/types.cpp b/neosensor/libraries/ArduinoJson/extras/tests/JsonVariant/types.cpp
index e3794990..42242991 100644
--- a/neosensor/libraries/ArduinoJson/extras/tests/JsonVariant/types.cpp
+++ b/neosensor/libraries/ArduinoJson/extras/tests/JsonVariant/types.cpp
@@ -1,5 +1,5 @@
 // ArduinoJson - https://arduinojson.org
-// Copyright © 2014-2022, Benoit BLANCHON
+// Copyright © 2014-2023, Benoit BLANCHON
 // MIT License
 
 #include <ArduinoJson.h>
diff --git a/neosensor/libraries/ArduinoJson/extras/tests/JsonVariant/unbound.cpp b/neosensor/libraries/ArduinoJson/extras/tests/JsonVariant/unbound.cpp
index 8089e1cc..90203236 100644
--- a/neosensor/libraries/ArduinoJson/extras/tests/JsonVariant/unbound.cpp
+++ b/neosensor/libraries/ArduinoJson/extras/tests/JsonVariant/unbound.cpp
@@ -1,5 +1,5 @@
 // ArduinoJson - https://arduinojson.org
-// Copyright © 2014-2022, Benoit BLANCHON
+// Copyright © 2014-2023, Benoit BLANCHON
 // MIT License
 
 #include <ArduinoJson.h>
diff --git a/neosensor/libraries/ArduinoJson/extras/tests/MemoryPool/CMakeLists.txt b/neosensor/libraries/ArduinoJson/extras/tests/MemoryPool/CMakeLists.txt
index 80ba3c6d..2f809cc2 100644
--- a/neosensor/libraries/ArduinoJson/extras/tests/MemoryPool/CMakeLists.txt
+++ b/neosensor/libraries/ArduinoJson/extras/tests/MemoryPool/CMakeLists.txt
@@ -1,8 +1,8 @@
 # ArduinoJson - https://arduinojson.org
-# Copyright © 2014-2022, Benoit BLANCHON
+# Copyright © 2014-2023, Benoit BLANCHON
 # MIT License
 
-add_executable(MemoryPoolTests 
+add_executable(MemoryPoolTests
 	allocVariant.cpp
 	clear.cpp
 	saveString.cpp
@@ -14,5 +14,5 @@ add_test(MemoryPool MemoryPoolTests)
 
 set_tests_properties(MemoryPool
 	PROPERTIES
-		LABELS 		"Catch"
+		LABELS "Catch"
 )
diff --git a/neosensor/libraries/ArduinoJson/extras/tests/MemoryPool/StringCopier.cpp b/neosensor/libraries/ArduinoJson/extras/tests/MemoryPool/StringCopier.cpp
index c6159053..0342bc4f 100644
--- a/neosensor/libraries/ArduinoJson/extras/tests/MemoryPool/StringCopier.cpp
+++ b/neosensor/libraries/ArduinoJson/extras/tests/MemoryPool/StringCopier.cpp
@@ -1,11 +1,11 @@
 // ArduinoJson - https://arduinojson.org
-// Copyright © 2014-2022, Benoit BLANCHON
+// Copyright © 2014-2023, Benoit BLANCHON
 // MIT License
 
 #include <ArduinoJson/StringStorage/StringCopier.hpp>
 #include <catch.hpp>
 
-using namespace ARDUINOJSON_NAMESPACE;
+using namespace ArduinoJson::detail;
 
 TEST_CASE("StringCopier") {
   char buffer[4096];
diff --git a/neosensor/libraries/ArduinoJson/extras/tests/MemoryPool/allocVariant.cpp b/neosensor/libraries/ArduinoJson/extras/tests/MemoryPool/allocVariant.cpp
index 606d6d7b..5123a4e2 100644
--- a/neosensor/libraries/ArduinoJson/extras/tests/MemoryPool/allocVariant.cpp
+++ b/neosensor/libraries/ArduinoJson/extras/tests/MemoryPool/allocVariant.cpp
@@ -1,11 +1,11 @@
 // ArduinoJson - https://arduinojson.org
-// Copyright © 2014-2022, Benoit BLANCHON
+// Copyright © 2014-2023, Benoit BLANCHON
 // MIT License
 
 #include <ArduinoJson/Memory/MemoryPool.hpp>
 #include <catch.hpp>
 
-using namespace ARDUINOJSON_NAMESPACE;
+using namespace ArduinoJson::detail;
 
 TEST_CASE("MemoryPool::allocVariant()") {
   char buffer[4096];
diff --git a/neosensor/libraries/ArduinoJson/extras/tests/MemoryPool/clear.cpp b/neosensor/libraries/ArduinoJson/extras/tests/MemoryPool/clear.cpp
index 66e7775c..9e33b471 100644
--- a/neosensor/libraries/ArduinoJson/extras/tests/MemoryPool/clear.cpp
+++ b/neosensor/libraries/ArduinoJson/extras/tests/MemoryPool/clear.cpp
@@ -1,12 +1,12 @@
 // ArduinoJson - https://arduinojson.org
-// Copyright © 2014-2022, Benoit BLANCHON
+// Copyright © 2014-2023, Benoit BLANCHON
 // MIT License
 
 #include <ArduinoJson/Memory/MemoryPool.hpp>
 #include <ArduinoJson/Strings/StringAdapters.hpp>
 #include <catch.hpp>
 
-using namespace ARDUINOJSON_NAMESPACE;
+using namespace ArduinoJson::detail;
 
 static const size_t poolCapacity = 512;
 
diff --git a/neosensor/libraries/ArduinoJson/extras/tests/MemoryPool/saveString.cpp b/neosensor/libraries/ArduinoJson/extras/tests/MemoryPool/saveString.cpp
index 7e0548cc..ba8d5b91 100644
--- a/neosensor/libraries/ArduinoJson/extras/tests/MemoryPool/saveString.cpp
+++ b/neosensor/libraries/ArduinoJson/extras/tests/MemoryPool/saveString.cpp
@@ -1,12 +1,12 @@
 // ArduinoJson - https://arduinojson.org
-// Copyright © 2014-2022, Benoit BLANCHON
+// Copyright © 2014-2023, Benoit BLANCHON
 // MIT License
 
 #include <ArduinoJson/Memory/MemoryPool.hpp>
 #include <ArduinoJson/Strings/StringAdapters.hpp>
 #include <catch.hpp>
 
-using namespace ARDUINOJSON_NAMESPACE;
+using namespace ArduinoJson::detail;
 
 static const char* saveString(MemoryPool& pool, const char* s) {
   return pool.saveString(adaptString(const_cast<char*>(s)));
diff --git a/neosensor/libraries/ArduinoJson/extras/tests/MemoryPool/size.cpp b/neosensor/libraries/ArduinoJson/extras/tests/MemoryPool/size.cpp
index 5179b105..057f4ab3 100644
--- a/neosensor/libraries/ArduinoJson/extras/tests/MemoryPool/size.cpp
+++ b/neosensor/libraries/ArduinoJson/extras/tests/MemoryPool/size.cpp
@@ -1,11 +1,11 @@
 // ArduinoJson - https://arduinojson.org
-// Copyright © 2014-2022, Benoit BLANCHON
+// Copyright © 2014-2023, Benoit BLANCHON
 // MIT License
 
 #include <ArduinoJson/Memory/MemoryPool.hpp>
 #include <catch.hpp>
 
-using namespace ARDUINOJSON_NAMESPACE;
+using namespace ArduinoJson::detail;
 
 TEST_CASE("MemoryPool::capacity()") {
   char buffer[4096];
diff --git a/neosensor/libraries/ArduinoJson/extras/tests/Misc/CMakeLists.txt b/neosensor/libraries/ArduinoJson/extras/tests/Misc/CMakeLists.txt
index d567efbd..5655a089 100644
--- a/neosensor/libraries/ArduinoJson/extras/tests/Misc/CMakeLists.txt
+++ b/neosensor/libraries/ArduinoJson/extras/tests/Misc/CMakeLists.txt
@@ -1,5 +1,5 @@
 # ArduinoJson - https://arduinojson.org
-# Copyright © 2014-2022, Benoit BLANCHON
+# Copyright © 2014-2023, Benoit BLANCHON
 # MIT License
 
 add_executable(MiscTests
@@ -25,5 +25,5 @@ add_test(Misc MiscTests)
 
 set_tests_properties(Misc
 	PROPERTIES
-		LABELS 		"Catch"
+		LABELS "Catch"
 )
diff --git a/neosensor/libraries/ArduinoJson/extras/tests/Misc/FloatParts.cpp b/neosensor/libraries/ArduinoJson/extras/tests/Misc/FloatParts.cpp
index 736b176c..8e3d82e1 100644
--- a/neosensor/libraries/ArduinoJson/extras/tests/Misc/FloatParts.cpp
+++ b/neosensor/libraries/ArduinoJson/extras/tests/Misc/FloatParts.cpp
@@ -1,11 +1,11 @@
 // ArduinoJson - https://arduinojson.org
-// Copyright © 2014-2022, Benoit BLANCHON
+// Copyright © 2014-2023, Benoit BLANCHON
 // MIT License
 
 #include <ArduinoJson/Numbers/FloatParts.hpp>
 #include <catch.hpp>
 
-using namespace ARDUINOJSON_NAMESPACE;
+using namespace ArduinoJson::detail;
 
 TEST_CASE("FloatParts<double>") {
   SECTION("1.7976931348623157E+308") {
diff --git a/neosensor/libraries/ArduinoJson/extras/tests/Misc/JsonString.cpp b/neosensor/libraries/ArduinoJson/extras/tests/Misc/JsonString.cpp
index 557f9936..c3dca172 100644
--- a/neosensor/libraries/ArduinoJson/extras/tests/Misc/JsonString.cpp
+++ b/neosensor/libraries/ArduinoJson/extras/tests/Misc/JsonString.cpp
@@ -1,5 +1,5 @@
 // ArduinoJson - https://arduinojson.org
-// Copyright © 2014-2022, Benoit BLANCHON
+// Copyright © 2014-2023, Benoit BLANCHON
 // MIT License
 
 #include <ArduinoJson.h>
diff --git a/neosensor/libraries/ArduinoJson/extras/tests/Misc/NoArduinoHeader.cpp b/neosensor/libraries/ArduinoJson/extras/tests/Misc/NoArduinoHeader.cpp
index 43a82b24..4b0218bf 100644
--- a/neosensor/libraries/ArduinoJson/extras/tests/Misc/NoArduinoHeader.cpp
+++ b/neosensor/libraries/ArduinoJson/extras/tests/Misc/NoArduinoHeader.cpp
@@ -1,5 +1,5 @@
 // ArduinoJson - https://arduinojson.org
-// Copyright © 2014-2022, Benoit BLANCHON
+// Copyright © 2014-2023, Benoit BLANCHON
 // MIT License
 
 #define ARDUINO 1
diff --git a/neosensor/libraries/ArduinoJson/extras/tests/Misc/Readers.cpp b/neosensor/libraries/ArduinoJson/extras/tests/Misc/Readers.cpp
index e46ad1e7..0b8f4d50 100644
--- a/neosensor/libraries/ArduinoJson/extras/tests/Misc/Readers.cpp
+++ b/neosensor/libraries/ArduinoJson/extras/tests/Misc/Readers.cpp
@@ -1,12 +1,14 @@
 // ArduinoJson - https://arduinojson.org
-// Copyright © 2014-2022, Benoit BLANCHON
+// Copyright © 2014-2023, Benoit BLANCHON
 // MIT License
 
 #include <Arduino.h>
 #include <ArduinoJson.hpp>
 #include <catch.hpp>
 
-using namespace ARDUINOJSON_NAMESPACE;
+#include <sstream>
+
+using namespace ArduinoJson::detail;
 
 TEST_CASE("Reader<std::istringstream>") {
   SECTION("read()") {
@@ -168,19 +170,19 @@ TEST_CASE("IteratorReader") {
 
 class StreamStub : public Stream {
  public:
-  StreamStub(const char* s) : _stream(s) {}
+  StreamStub(const char* s) : stream_(s) {}
 
   int read() {
-    return _stream.get();
+    return stream_.get();
   }
 
   size_t readBytes(char* buffer, size_t length) {
-    _stream.read(buffer, static_cast<std::streamsize>(length));
-    return static_cast<size_t>(_stream.gcount());
+    stream_.read(buffer, static_cast<std::streamsize>(length));
+    return static_cast<size_t>(stream_.gcount());
   }
 
  private:
-  std::istringstream _stream;
+  std::istringstream stream_;
 };
 
 TEST_CASE("Reader<Stream>") {
diff --git a/neosensor/libraries/ArduinoJson/extras/tests/Misc/StringAdapters.cpp b/neosensor/libraries/ArduinoJson/extras/tests/Misc/StringAdapters.cpp
index 02282334..1c77e08b 100644
--- a/neosensor/libraries/ArduinoJson/extras/tests/Misc/StringAdapters.cpp
+++ b/neosensor/libraries/ArduinoJson/extras/tests/Misc/StringAdapters.cpp
@@ -1,19 +1,18 @@
 // ArduinoJson - https://arduinojson.org
-// Copyright © 2014-2022, Benoit BLANCHON
+// Copyright © 2014-2023, Benoit BLANCHON
 // MIT License
 
-#define ARDUINOJSON_ENABLE_PROGMEM 1
-#define ARDUINOJSON_ENABLE_ARDUINO_STRING 1
-
-#include "custom_string.hpp"
-#include "weird_strcmp.hpp"
+#include <Arduino.h>
 
 #include <ArduinoJson/Strings/IsString.hpp>
 #include <ArduinoJson/Strings/StringAdapters.hpp>
 
 #include <catch.hpp>
 
-using namespace ARDUINOJSON_NAMESPACE;
+#include "custom_string.hpp"
+#include "weird_strcmp.hpp"
+
+using namespace ArduinoJson::detail;
 
 TEST_CASE("ZeroTerminatedRamString") {
   SECTION("null") {
@@ -90,13 +89,13 @@ struct EmptyStruct {};
 
 TEST_CASE("IsString<T>") {
   CHECK(IsString<std::string>::value == true);
-  CHECK(IsString<std::basic_string<wchar_t> >::value == false);
+  CHECK(IsString<std::basic_string<wchar_t>>::value == false);
   CHECK(IsString<custom_string>::value == true);
   CHECK(IsString<const __FlashStringHelper*>::value == true);
   CHECK(IsString<const char*>::value == true);
   CHECK(IsString<const char[8]>::value == true);
-  CHECK(IsString< ::String>::value == true);
-  CHECK(IsString< ::StringSumHelper>::value == true);
+  CHECK(IsString<::String>::value == true);
+  CHECK(IsString<::StringSumHelper>::value == true);
   CHECK(IsString<const EmptyStruct*>::value == false);
 }
 
diff --git a/neosensor/libraries/ArduinoJson/extras/tests/Misc/StringWriter.cpp b/neosensor/libraries/ArduinoJson/extras/tests/Misc/StringWriter.cpp
index bdb5bdb1..81cb88e9 100644
--- a/neosensor/libraries/ArduinoJson/extras/tests/Misc/StringWriter.cpp
+++ b/neosensor/libraries/ArduinoJson/extras/tests/Misc/StringWriter.cpp
@@ -1,14 +1,17 @@
 // ArduinoJson - https://arduinojson.org
-// Copyright © 2014-2022, Benoit BLANCHON
+// Copyright © 2014-2023, Benoit BLANCHON
 // MIT License
 
-#define ARDUINOJSON_ENABLE_ARDUINO_STRING 1
+#include <Arduino.h>
+
 #define ARDUINOJSON_STRING_BUFFER_SIZE 5
 #include <ArduinoJson.h>
+
 #include <catch.hpp>
+
 #include "custom_string.hpp"
 
-using namespace ARDUINOJSON_NAMESPACE;
+using namespace ArduinoJson::detail;
 
 template <typename StringWriter>
 static size_t print(StringWriter& writer, const char* s) {
@@ -65,7 +68,7 @@ TEST_CASE("Writer<std::string>") {
 
 TEST_CASE("Writer<String>") {
   ::String output;
-  Writer< ::String> writer(output);
+  Writer<::String> writer(output);
 
   SECTION("write(char)") {
     SECTION("writes to temporary buffer") {
diff --git a/neosensor/libraries/ArduinoJson/extras/tests/Misc/TypeTraits.cpp b/neosensor/libraries/ArduinoJson/extras/tests/Misc/TypeTraits.cpp
index 76ec4861..10b6168b 100644
--- a/neosensor/libraries/ArduinoJson/extras/tests/Misc/TypeTraits.cpp
+++ b/neosensor/libraries/ArduinoJson/extras/tests/Misc/TypeTraits.cpp
@@ -1,11 +1,13 @@
 // ArduinoJson - https://arduinojson.org
-// Copyright © 2014-2022, Benoit BLANCHON
+// Copyright © 2014-2023, Benoit BLANCHON
 // MIT License
 
 #include <ArduinoJson.h>
 #include <catch.hpp>
 
-using namespace ARDUINOJSON_NAMESPACE;
+#include <sstream>
+
+using namespace ArduinoJson::detail;
 
 class EmptyClass {};
 enum EmptyEnum {};
@@ -19,9 +21,9 @@ TEST_CASE("Polyfills/type_traits") {
   }
 
   SECTION("is_array") {
-    REQUIRE_FALSE((is_array<const char*>::value));
-    REQUIRE((is_array<const char[]>::value));
-    REQUIRE((is_array<const char[10]>::value));
+    REQUIRE_FALSE(is_array<const char*>::value);
+    REQUIRE(is_array<const char[]>::value);
+    REQUIRE(is_array<const char[10]>::value);
   }
 
   SECTION("is_const") {
@@ -172,36 +174,35 @@ TEST_CASE("Polyfills/type_traits") {
   }
 
   SECTION("is_convertible") {
-    CHECK((is_convertible<short, int>::value == true));
-    CHECK((is_convertible<int, int>::value == true));
-    CHECK((is_convertible<EmptyEnum, int>::value == true));
-    CHECK((is_convertible<int*, int>::value == false));
-    CHECK((is_convertible<EmptyClass, int>::value == false));
-
-    CHECK((is_convertible<DeserializationError, JsonVariantConst>::value ==
-           false));
-    CHECK((is_convertible<JsonPair, JsonVariantConst>::value == false));
-    CHECK((is_convertible<JsonVariant, JsonVariantConst>::value == true));
-    CHECK((is_convertible<JsonVariantConst, JsonVariantConst>::value == true));
-    CHECK((is_convertible<JsonArray, JsonVariantConst>::value == true));
-    CHECK((is_convertible<ElementProxy<JsonArray>, JsonVariantConst>::value ==
-           true));
-    CHECK((is_convertible<JsonArrayConst, JsonVariantConst>::value == true));
-    CHECK((is_convertible<JsonObject, JsonVariantConst>::value == true));
-    CHECK((is_convertible<MemberProxy<JsonObject, const char*>,
-                          JsonVariantConst>::value == true));
-    CHECK((is_convertible<JsonObjectConst, JsonVariantConst>::value == true));
-    CHECK(
-        (is_convertible<DynamicJsonDocument, JsonVariantConst>::value == true));
-    CHECK((is_convertible<StaticJsonDocument<10>, JsonVariantConst>::value ==
-           true));
+    CHECK(is_convertible<short, int>::value == true);
+    CHECK(is_convertible<int, int>::value == true);
+    CHECK(is_convertible<EmptyEnum, int>::value == true);
+    CHECK(is_convertible<int*, int>::value == false);
+    CHECK(is_convertible<EmptyClass, int>::value == false);
+
+    CHECK(is_convertible<DeserializationError, JsonVariantConst>::value ==
+          false);
+    CHECK(is_convertible<JsonPair, JsonVariantConst>::value == false);
+    CHECK(is_convertible<JsonVariant, JsonVariantConst>::value == true);
+    CHECK(is_convertible<JsonVariantConst, JsonVariantConst>::value == true);
+    CHECK(is_convertible<JsonArray, JsonVariantConst>::value == true);
+    CHECK(is_convertible<ElementProxy<JsonArray>, JsonVariantConst>::value ==
+          true);
+    CHECK(is_convertible<JsonArrayConst, JsonVariantConst>::value == true);
+    CHECK(is_convertible<JsonObject, JsonVariantConst>::value == true);
+    CHECK(is_convertible<MemberProxy<JsonObject, const char*>,
+                         JsonVariantConst>::value == true);
+    CHECK(is_convertible<JsonObjectConst, JsonVariantConst>::value == true);
+    CHECK(is_convertible<DynamicJsonDocument, JsonVariantConst>::value == true);
+    CHECK(is_convertible<StaticJsonDocument<10>, JsonVariantConst>::value ==
+          true);
   }
 
   SECTION("is_class") {
-    CHECK((is_class<int>::value == false));
-    CHECK((is_class<EmptyEnum>::value == false));
-    CHECK((is_class<int*>::value == false));
-    CHECK((is_class<EmptyClass>::value == true));
+    CHECK(is_class<int>::value == false);
+    CHECK(is_class<EmptyEnum>::value == false);
+    CHECK(is_class<int*>::value == false);
+    CHECK(is_class<EmptyClass>::value == true);
   }
 
   SECTION("is_enum") {
diff --git a/neosensor/libraries/ArduinoJson/extras/tests/Misc/Utf16.cpp b/neosensor/libraries/ArduinoJson/extras/tests/Misc/Utf16.cpp
index 31ab7ec5..3a595910 100644
--- a/neosensor/libraries/ArduinoJson/extras/tests/Misc/Utf16.cpp
+++ b/neosensor/libraries/ArduinoJson/extras/tests/Misc/Utf16.cpp
@@ -1,11 +1,11 @@
 // ArduinoJson - https://arduinojson.org
-// Copyright © 2014-2022, Benoit BLANCHON
+// Copyright © 2014-2023, Benoit BLANCHON
 // MIT License
 
 #include <ArduinoJson/Json/Utf16.hpp>
 #include <catch.hpp>
 
-using namespace ARDUINOJSON_NAMESPACE;
+using namespace ArduinoJson::detail;
 
 static void testUtf16Codepoint(uint16_t codeunit, uint32_t expectedCodepoint) {
   Utf16::Codepoint cp;
diff --git a/neosensor/libraries/ArduinoJson/extras/tests/Misc/Utf8.cpp b/neosensor/libraries/ArduinoJson/extras/tests/Misc/Utf8.cpp
index b44028ae..0c62033d 100644
--- a/neosensor/libraries/ArduinoJson/extras/tests/Misc/Utf8.cpp
+++ b/neosensor/libraries/ArduinoJson/extras/tests/Misc/Utf8.cpp
@@ -1,5 +1,5 @@
 // ArduinoJson - https://arduinojson.org
-// Copyright © 2014-2022, Benoit BLANCHON
+// Copyright © 2014-2023, Benoit BLANCHON
 // MIT License
 
 #include <ArduinoJson.h>
@@ -7,7 +7,7 @@
 
 #include <string>
 
-using namespace ARDUINOJSON_NAMESPACE;
+using namespace ArduinoJson::detail;
 
 static void testCodepoint(uint32_t codepoint, std::string expected) {
   char buffer[4096];
diff --git a/neosensor/libraries/ArduinoJson/extras/tests/Misc/arithmeticCompare.cpp b/neosensor/libraries/ArduinoJson/extras/tests/Misc/arithmeticCompare.cpp
index a67943e8..2d51754a 100644
--- a/neosensor/libraries/ArduinoJson/extras/tests/Misc/arithmeticCompare.cpp
+++ b/neosensor/libraries/ArduinoJson/extras/tests/Misc/arithmeticCompare.cpp
@@ -1,103 +1,97 @@
 // ArduinoJson - https://arduinojson.org
-// Copyright © 2014-2022, Benoit BLANCHON
+// Copyright © 2014-2023, Benoit BLANCHON
 // MIT License
 
 #include <ArduinoJson/Numbers/arithmeticCompare.hpp>
 #include <catch.hpp>
 
-using namespace ARDUINOJSON_NAMESPACE;
+using namespace ArduinoJson::detail;
 
 TEST_CASE("arithmeticCompare()") {
   SECTION("int vs uint8_t") {
-    CHECK((arithmeticCompare<int, uint8_t>(256, 1) == COMPARE_RESULT_GREATER));
-    CHECK((arithmeticCompare<int, uint8_t>(41, 42) == COMPARE_RESULT_LESS));
-    CHECK((arithmeticCompare<int, uint8_t>(42, 42) == COMPARE_RESULT_EQUAL));
-    CHECK((arithmeticCompare<int, uint8_t>(43, 42) == COMPARE_RESULT_GREATER));
+    CHECK(arithmeticCompare<int, uint8_t>(256, 1) == COMPARE_RESULT_GREATER);
+    CHECK(arithmeticCompare<int, uint8_t>(41, 42) == COMPARE_RESULT_LESS);
+    CHECK(arithmeticCompare<int, uint8_t>(42, 42) == COMPARE_RESULT_EQUAL);
+    CHECK(arithmeticCompare<int, uint8_t>(43, 42) == COMPARE_RESULT_GREATER);
   }
 
   SECTION("unsigned vs int") {
-    CHECK((arithmeticCompare<unsigned, int>(0, -1) == COMPARE_RESULT_GREATER));
-    CHECK((arithmeticCompare<unsigned, int>(42, 41) == COMPARE_RESULT_GREATER));
-    CHECK((arithmeticCompare<unsigned, int>(42, 42) == COMPARE_RESULT_EQUAL));
-    CHECK((arithmeticCompare<unsigned, int>(42, 43) == COMPARE_RESULT_LESS));
+    CHECK(arithmeticCompare<unsigned, int>(0, -1) == COMPARE_RESULT_GREATER);
+    CHECK(arithmeticCompare<unsigned, int>(42, 41) == COMPARE_RESULT_GREATER);
+    CHECK(arithmeticCompare<unsigned, int>(42, 42) == COMPARE_RESULT_EQUAL);
+    CHECK(arithmeticCompare<unsigned, int>(42, 43) == COMPARE_RESULT_LESS);
   }
 
   SECTION("float vs int") {
-    CHECK((arithmeticCompare<float, int>(42, 41) == COMPARE_RESULT_GREATER));
-    CHECK((arithmeticCompare<float, int>(42, 42) == COMPARE_RESULT_EQUAL));
-    CHECK((arithmeticCompare<float, int>(42, 43) == COMPARE_RESULT_LESS));
+    CHECK(arithmeticCompare<float, int>(42, 41) == COMPARE_RESULT_GREATER);
+    CHECK(arithmeticCompare<float, int>(42, 42) == COMPARE_RESULT_EQUAL);
+    CHECK(arithmeticCompare<float, int>(42, 43) == COMPARE_RESULT_LESS);
   }
 
   SECTION("int vs unsigned") {
-    CHECK((arithmeticCompare<int, unsigned>(-1, 0) == COMPARE_RESULT_LESS));
-    CHECK((arithmeticCompare<int, unsigned>(0, 0) == COMPARE_RESULT_EQUAL));
-    CHECK((arithmeticCompare<int, unsigned>(1, 0) == COMPARE_RESULT_GREATER));
-    CHECK((arithmeticCompare<int, unsigned>(42, 41) == COMPARE_RESULT_GREATER));
-    CHECK((arithmeticCompare<int, unsigned>(42, 42) == COMPARE_RESULT_EQUAL));
-    CHECK((arithmeticCompare<int, unsigned>(42, 43) == COMPARE_RESULT_LESS));
+    CHECK(arithmeticCompare<int, unsigned>(-1, 0) == COMPARE_RESULT_LESS);
+    CHECK(arithmeticCompare<int, unsigned>(0, 0) == COMPARE_RESULT_EQUAL);
+    CHECK(arithmeticCompare<int, unsigned>(1, 0) == COMPARE_RESULT_GREATER);
+    CHECK(arithmeticCompare<int, unsigned>(42, 41) == COMPARE_RESULT_GREATER);
+    CHECK(arithmeticCompare<int, unsigned>(42, 42) == COMPARE_RESULT_EQUAL);
+    CHECK(arithmeticCompare<int, unsigned>(42, 43) == COMPARE_RESULT_LESS);
   }
 
   SECTION("unsigned vs unsigned") {
-    CHECK((arithmeticCompare<unsigned, unsigned>(42, 41) ==
-           COMPARE_RESULT_GREATER));
-    CHECK((arithmeticCompare<unsigned, unsigned>(42, 42) ==
-           COMPARE_RESULT_EQUAL));
-    CHECK(
-        (arithmeticCompare<unsigned, unsigned>(42, 43) == COMPARE_RESULT_LESS));
+    CHECK(arithmeticCompare<unsigned, unsigned>(42, 41) ==
+          COMPARE_RESULT_GREATER);
+    CHECK(arithmeticCompare<unsigned, unsigned>(42, 42) ==
+          COMPARE_RESULT_EQUAL);
+    CHECK(arithmeticCompare<unsigned, unsigned>(42, 43) == COMPARE_RESULT_LESS);
   }
 
   SECTION("bool vs bool") {
-    CHECK(
-        (arithmeticCompare<bool, bool>(false, false) == COMPARE_RESULT_EQUAL));
-    CHECK((arithmeticCompare<bool, bool>(true, true) == COMPARE_RESULT_EQUAL));
-    CHECK((arithmeticCompare<bool, bool>(false, true) == COMPARE_RESULT_LESS));
-    CHECK(
-        (arithmeticCompare<bool, bool>(true, false) == COMPARE_RESULT_GREATER));
+    CHECK(arithmeticCompare<bool, bool>(false, false) == COMPARE_RESULT_EQUAL);
+    CHECK(arithmeticCompare<bool, bool>(true, true) == COMPARE_RESULT_EQUAL);
+    CHECK(arithmeticCompare<bool, bool>(false, true) == COMPARE_RESULT_LESS);
+    CHECK(arithmeticCompare<bool, bool>(true, false) == COMPARE_RESULT_GREATER);
   }
 
   SECTION("bool vs int") {
-    CHECK((arithmeticCompare<bool, int>(false, -1) == COMPARE_RESULT_GREATER));
-    CHECK((arithmeticCompare<bool, int>(false, 0) == COMPARE_RESULT_EQUAL));
-    CHECK((arithmeticCompare<bool, int>(false, 1) == COMPARE_RESULT_LESS));
-    CHECK((arithmeticCompare<bool, int>(true, 0) == COMPARE_RESULT_GREATER));
-    CHECK((arithmeticCompare<bool, int>(true, 1) == COMPARE_RESULT_EQUAL));
-    CHECK((arithmeticCompare<bool, int>(true, 2) == COMPARE_RESULT_LESS));
+    CHECK(arithmeticCompare<bool, int>(false, -1) == COMPARE_RESULT_GREATER);
+    CHECK(arithmeticCompare<bool, int>(false, 0) == COMPARE_RESULT_EQUAL);
+    CHECK(arithmeticCompare<bool, int>(false, 1) == COMPARE_RESULT_LESS);
+    CHECK(arithmeticCompare<bool, int>(true, 0) == COMPARE_RESULT_GREATER);
+    CHECK(arithmeticCompare<bool, int>(true, 1) == COMPARE_RESULT_EQUAL);
+    CHECK(arithmeticCompare<bool, int>(true, 2) == COMPARE_RESULT_LESS);
   }
 
   SECTION("bool vs int") {
-    CHECK((arithmeticCompare<int, bool>(0, false) == COMPARE_RESULT_EQUAL));
-    CHECK((arithmeticCompare<int, bool>(1, true) == COMPARE_RESULT_EQUAL));
-    CHECK((arithmeticCompare<int, bool>(1, false) == COMPARE_RESULT_GREATER));
-    CHECK((arithmeticCompare<int, bool>(0, true) == COMPARE_RESULT_LESS));
+    CHECK(arithmeticCompare<int, bool>(0, false) == COMPARE_RESULT_EQUAL);
+    CHECK(arithmeticCompare<int, bool>(1, true) == COMPARE_RESULT_EQUAL);
+    CHECK(arithmeticCompare<int, bool>(1, false) == COMPARE_RESULT_GREATER);
+    CHECK(arithmeticCompare<int, bool>(0, true) == COMPARE_RESULT_LESS);
   }
 }
 
 TEST_CASE("arithmeticCompareNegateLeft()") {
   SECTION("unsigned vs int") {
-    CHECK((arithmeticCompareNegateLeft<int>(0, 1) == COMPARE_RESULT_LESS));
-    CHECK((arithmeticCompareNegateLeft<int>(42, -41) == COMPARE_RESULT_LESS));
-    CHECK((arithmeticCompareNegateLeft<int>(42, -42) == COMPARE_RESULT_EQUAL));
-    CHECK(
-        (arithmeticCompareNegateLeft<int>(42, -43) == COMPARE_RESULT_GREATER));
+    CHECK(arithmeticCompareNegateLeft<int>(0, 1) == COMPARE_RESULT_LESS);
+    CHECK(arithmeticCompareNegateLeft<int>(42, -41) == COMPARE_RESULT_LESS);
+    CHECK(arithmeticCompareNegateLeft<int>(42, -42) == COMPARE_RESULT_EQUAL);
+    CHECK(arithmeticCompareNegateLeft<int>(42, -43) == COMPARE_RESULT_GREATER);
   }
 
   SECTION("unsigned vs unsigned") {
-    CHECK(
-        (arithmeticCompareNegateLeft<unsigned>(42, 42) == COMPARE_RESULT_LESS));
+    CHECK(arithmeticCompareNegateLeft<unsigned>(42, 42) == COMPARE_RESULT_LESS);
   }
 }
 
 TEST_CASE("arithmeticCompareNegateRight()") {
   SECTION("int vs unsigned") {
-    CHECK((arithmeticCompareNegateRight<int>(1, 0) == COMPARE_RESULT_GREATER));
-    CHECK(
-        (arithmeticCompareNegateRight<int>(-41, 42) == COMPARE_RESULT_GREATER));
-    CHECK((arithmeticCompareNegateRight<int>(-42, 42) == COMPARE_RESULT_EQUAL));
-    CHECK((arithmeticCompareNegateRight<int>(-43, 42) == COMPARE_RESULT_LESS));
+    CHECK(arithmeticCompareNegateRight<int>(1, 0) == COMPARE_RESULT_GREATER);
+    CHECK(arithmeticCompareNegateRight<int>(-41, 42) == COMPARE_RESULT_GREATER);
+    CHECK(arithmeticCompareNegateRight<int>(-42, 42) == COMPARE_RESULT_EQUAL);
+    CHECK(arithmeticCompareNegateRight<int>(-43, 42) == COMPARE_RESULT_LESS);
   }
 
   SECTION("unsigned vs unsigned") {
-    CHECK((arithmeticCompareNegateRight<unsigned>(42, 42) ==
-           COMPARE_RESULT_GREATER));
+    CHECK(arithmeticCompareNegateRight<unsigned>(42, 42) ==
+          COMPARE_RESULT_GREATER);
   }
 }
diff --git a/neosensor/libraries/ArduinoJson/extras/tests/Misc/conflicts.cpp b/neosensor/libraries/ArduinoJson/extras/tests/Misc/conflicts.cpp
index 991a94ef..2e33ea10 100644
--- a/neosensor/libraries/ArduinoJson/extras/tests/Misc/conflicts.cpp
+++ b/neosensor/libraries/ArduinoJson/extras/tests/Misc/conflicts.cpp
@@ -1,5 +1,5 @@
 // ArduinoJson - https://arduinojson.org
-// Copyright © 2014-2022, Benoit BLANCHON
+// Copyright © 2014-2023, Benoit BLANCHON
 // MIT License
 
 // Include any header that might use the conflicting macros
@@ -52,5 +52,8 @@
 #define BLOCKSIZE
 #define CAPACITY
 
+// issue #1905
+#define _current
+
 // catch.hpp mutes several warnings, this file also allows to detect them
 #include "ArduinoJson.h"
diff --git a/neosensor/libraries/ArduinoJson/extras/tests/Misc/custom_string.hpp b/neosensor/libraries/ArduinoJson/extras/tests/Misc/custom_string.hpp
index 3c5b7c14..4a6a5a73 100644
--- a/neosensor/libraries/ArduinoJson/extras/tests/Misc/custom_string.hpp
+++ b/neosensor/libraries/ArduinoJson/extras/tests/Misc/custom_string.hpp
@@ -1,5 +1,5 @@
 // ArduinoJson - https://arduinojson.org
-// Copyright © 2014-2022, Benoit BLANCHON
+// Copyright © 2014-2023, Benoit BLANCHON
 // MIT License
 
 #pragma once
diff --git a/neosensor/libraries/ArduinoJson/extras/tests/Misc/printable.cpp b/neosensor/libraries/ArduinoJson/extras/tests/Misc/printable.cpp
index 7cec8f93..09c23ad1 100644
--- a/neosensor/libraries/ArduinoJson/extras/tests/Misc/printable.cpp
+++ b/neosensor/libraries/ArduinoJson/extras/tests/Misc/printable.cpp
@@ -1,5 +1,5 @@
 // ArduinoJson - https://arduinojson.org
-// Copyright © 2014-2022, Benoit BLANCHON
+// Copyright © 2014-2023, Benoit BLANCHON
 // MIT License
 
 #include <Arduino.h>
@@ -29,21 +29,21 @@ struct PrintAllAtOnce {
 
 template <typename PrintPolicy>
 struct PrintableString : public Printable {
-  PrintableString(const char* s) : _str(s), _total(0) {}
+  PrintableString(const char* s) : str_(s), total_(0) {}
 
   virtual size_t printTo(Print& p) const {
-    size_t result = PrintPolicy::printStringTo(_str, p);
-    _total += result;
+    size_t result = PrintPolicy::printStringTo(str_, p);
+    total_ += result;
     return result;
   }
 
   size_t totalBytesWritten() const {
-    return _total;
+    return total_;
   }
 
  private:
-  std::string _str;
-  mutable size_t _total;
+  std::string str_;
+  mutable size_t total_;
 };
 
 TEST_CASE("Printable") {
diff --git a/neosensor/libraries/ArduinoJson/extras/tests/Misc/unsigned_char.cpp b/neosensor/libraries/ArduinoJson/extras/tests/Misc/unsigned_char.cpp
index 7a5e3eef..d3ee492a 100644
--- a/neosensor/libraries/ArduinoJson/extras/tests/Misc/unsigned_char.cpp
+++ b/neosensor/libraries/ArduinoJson/extras/tests/Misc/unsigned_char.cpp
@@ -1,5 +1,5 @@
 // ArduinoJson - https://arduinojson.org
-// Copyright © 2014-2022, Benoit BLANCHON
+// Copyright © 2014-2023, Benoit BLANCHON
 // MIT License
 
 #include <ArduinoJson.h>
diff --git a/neosensor/libraries/ArduinoJson/extras/tests/Misc/version.cpp b/neosensor/libraries/ArduinoJson/extras/tests/Misc/version.cpp
index 486a8670..a91f159a 100644
--- a/neosensor/libraries/ArduinoJson/extras/tests/Misc/version.cpp
+++ b/neosensor/libraries/ArduinoJson/extras/tests/Misc/version.cpp
@@ -1,5 +1,5 @@
 // ArduinoJson - https://arduinojson.org
-// Copyright © 2014-2022, Benoit BLANCHON
+// Copyright © 2014-2023, Benoit BLANCHON
 // MIT License
 
 #include <ArduinoJson/version.hpp>
diff --git a/neosensor/libraries/ArduinoJson/extras/tests/Misc/weird_strcmp.hpp b/neosensor/libraries/ArduinoJson/extras/tests/Misc/weird_strcmp.hpp
index ff755ef5..5463d7ec 100644
--- a/neosensor/libraries/ArduinoJson/extras/tests/Misc/weird_strcmp.hpp
+++ b/neosensor/libraries/ArduinoJson/extras/tests/Misc/weird_strcmp.hpp
@@ -1,5 +1,5 @@
 // ArduinoJson - https://arduinojson.org
-// Copyright © 2014-2022, Benoit BLANCHON
+// Copyright © 2014-2023, Benoit BLANCHON
 // MIT License
 
 #include <ArduinoJson/Namespace.hpp>
@@ -8,7 +8,8 @@
 
 // Issue #1198: strcmp() implementation that returns a value larger than 8-bit
 
-namespace ARDUINOJSON_NAMESPACE {
+ARDUINOJSON_BEGIN_PRIVATE_NAMESPACE
+
 int strcmp(const char* a, const char* b) {
   int result = ::strcmp(a, b);
   if (result > 0)
@@ -26,4 +27,5 @@ int strncmp(const char* a, const char* b, size_t n) {
     return -214748364;
   return 0;
 }
-}  // namespace ARDUINOJSON_NAMESPACE
+
+ARDUINOJSON_END_PRIVATE_NAMESPACE
diff --git a/neosensor/libraries/ArduinoJson/extras/tests/MixedConfiguration/CMakeLists.txt b/neosensor/libraries/ArduinoJson/extras/tests/MixedConfiguration/CMakeLists.txt
index 01d1216e..c2c9dc5c 100644
--- a/neosensor/libraries/ArduinoJson/extras/tests/MixedConfiguration/CMakeLists.txt
+++ b/neosensor/libraries/ArduinoJson/extras/tests/MixedConfiguration/CMakeLists.txt
@@ -1,5 +1,5 @@
 # ArduinoJson - https://arduinojson.org
-# Copyright © 2014-2022, Benoit BLANCHON
+# Copyright © 2014-2023, Benoit BLANCHON
 # MIT License
 
 add_executable(MixedConfigurationTests
@@ -19,6 +19,8 @@ add_executable(MixedConfigurationTests
 	issue1707.cpp
 	use_double_0.cpp
 	use_double_1.cpp
+	use_long_long_0.cpp
+	use_long_long_1.cpp
 )
 
 set_target_properties(MixedConfigurationTests PROPERTIES UNITY_BUILD OFF)
@@ -27,5 +29,5 @@ add_test(MixedConfiguration MixedConfigurationTests)
 
 set_tests_properties(MixedConfiguration
 	PROPERTIES
-		LABELS 		"Catch"
+		LABELS "Catch"
 )
diff --git a/neosensor/libraries/ArduinoJson/extras/tests/MixedConfiguration/enable_alignment_0.cpp b/neosensor/libraries/ArduinoJson/extras/tests/MixedConfiguration/enable_alignment_0.cpp
index 426f516a..569f84aa 100644
--- a/neosensor/libraries/ArduinoJson/extras/tests/MixedConfiguration/enable_alignment_0.cpp
+++ b/neosensor/libraries/ArduinoJson/extras/tests/MixedConfiguration/enable_alignment_0.cpp
@@ -1,11 +1,11 @@
-#define ARDUINOJSON_NAMESPACE ArduinoJson_NoAlignment
+#define ARDUINOJSON_VERSION_NAMESPACE NoAlignment
 #define ARDUINOJSON_ENABLE_ALIGNMENT 0
 #include <ArduinoJson.h>
 
 #include <catch.hpp>
 
 TEST_CASE("ARDUINOJSON_ENABLE_ALIGNMENT == 0") {
-  using namespace ARDUINOJSON_NAMESPACE;
+  using namespace ArduinoJson::detail;
 
   const size_t N = sizeof(void*);
 
diff --git a/neosensor/libraries/ArduinoJson/extras/tests/MixedConfiguration/enable_alignment_1.cpp b/neosensor/libraries/ArduinoJson/extras/tests/MixedConfiguration/enable_alignment_1.cpp
index 9abf50c8..56d48169 100644
--- a/neosensor/libraries/ArduinoJson/extras/tests/MixedConfiguration/enable_alignment_1.cpp
+++ b/neosensor/libraries/ArduinoJson/extras/tests/MixedConfiguration/enable_alignment_1.cpp
@@ -4,7 +4,7 @@
 #include <catch.hpp>
 
 TEST_CASE("ARDUINOJSON_ENABLE_ALIGNMENT == 1") {
-  using namespace ARDUINOJSON_NAMESPACE;
+  using namespace ArduinoJson::detail;
 
   const size_t N = sizeof(void*);
 
diff --git a/neosensor/libraries/ArduinoJson/extras/tests/MixedConfiguration/enable_comments_0.cpp b/neosensor/libraries/ArduinoJson/extras/tests/MixedConfiguration/enable_comments_0.cpp
index 6329021a..a3ada5ea 100644
--- a/neosensor/libraries/ArduinoJson/extras/tests/MixedConfiguration/enable_comments_0.cpp
+++ b/neosensor/libraries/ArduinoJson/extras/tests/MixedConfiguration/enable_comments_0.cpp
@@ -1,5 +1,5 @@
 // ArduinoJson - https://arduinojson.org
-// Copyright © 2014-2022, Benoit BLANCHON
+// Copyright © 2014-2023, Benoit BLANCHON
 // MIT License
 
 #define ARDUINOJSON_ENABLE_COMMENTS 0
diff --git a/neosensor/libraries/ArduinoJson/extras/tests/MixedConfiguration/enable_comments_1.cpp b/neosensor/libraries/ArduinoJson/extras/tests/MixedConfiguration/enable_comments_1.cpp
index 18462922..5e0831c5 100644
--- a/neosensor/libraries/ArduinoJson/extras/tests/MixedConfiguration/enable_comments_1.cpp
+++ b/neosensor/libraries/ArduinoJson/extras/tests/MixedConfiguration/enable_comments_1.cpp
@@ -1,5 +1,5 @@
 // ArduinoJson - https://arduinojson.org
-// Copyright © 2014-2022, Benoit BLANCHON
+// Copyright © 2014-2023, Benoit BLANCHON
 // MIT License
 
 #define ARDUINOJSON_ENABLE_COMMENTS 1
diff --git a/neosensor/libraries/ArduinoJson/extras/tests/MixedConfiguration/enable_infinity_1.cpp b/neosensor/libraries/ArduinoJson/extras/tests/MixedConfiguration/enable_infinity_1.cpp
index 19e0d5bf..28a3461f 100644
--- a/neosensor/libraries/ArduinoJson/extras/tests/MixedConfiguration/enable_infinity_1.cpp
+++ b/neosensor/libraries/ArduinoJson/extras/tests/MixedConfiguration/enable_infinity_1.cpp
@@ -5,7 +5,7 @@
 #include <limits>
 
 namespace my {
-using ARDUINOJSON_NAMESPACE::isinf;
+using ArduinoJson::detail::isinf;
 }  // namespace my
 
 TEST_CASE("ARDUINOJSON_ENABLE_INFINITY == 1") {
diff --git a/neosensor/libraries/ArduinoJson/extras/tests/MixedConfiguration/enable_nan_1.cpp b/neosensor/libraries/ArduinoJson/extras/tests/MixedConfiguration/enable_nan_1.cpp
index 34d94fca..f9ae04a6 100644
--- a/neosensor/libraries/ArduinoJson/extras/tests/MixedConfiguration/enable_nan_1.cpp
+++ b/neosensor/libraries/ArduinoJson/extras/tests/MixedConfiguration/enable_nan_1.cpp
@@ -5,7 +5,7 @@
 #include <limits>
 
 namespace my {
-using ARDUINOJSON_NAMESPACE::isnan;
+using ArduinoJson::detail::isnan;
 }  // namespace my
 
 TEST_CASE("ARDUINOJSON_ENABLE_NAN == 1") {
diff --git a/neosensor/libraries/ArduinoJson/extras/tests/MixedConfiguration/enable_progmem_1.cpp b/neosensor/libraries/ArduinoJson/extras/tests/MixedConfiguration/enable_progmem_1.cpp
index 9ddf7f26..2cc683d7 100644
--- a/neosensor/libraries/ArduinoJson/extras/tests/MixedConfiguration/enable_progmem_1.cpp
+++ b/neosensor/libraries/ArduinoJson/extras/tests/MixedConfiguration/enable_progmem_1.cpp
@@ -1,5 +1,5 @@
 // ArduinoJson - https://arduinojson.org
-// Copyright © 2014-2022, Benoit BLANCHON
+// Copyright © 2014-2023, Benoit BLANCHON
 // MIT License
 
 #define ARDUINOJSON_ENABLE_PROGMEM 1
@@ -51,7 +51,7 @@ TEST_CASE("Flash strings") {
 }
 
 TEST_CASE("parseNumber()") {  // tables are in Flash
-  using ARDUINOJSON_NAMESPACE::parseNumber;
+  using ArduinoJson::detail::parseNumber;
 
   CHECK(parseNumber<float>("1") == 1.f);
   CHECK(parseNumber<float>("1.23") == 1.23f);
@@ -93,7 +93,7 @@ TEST_CASE("memcpy_P") {
 }
 
 TEST_CASE("BoundedReader<const __FlashStringHelper*>") {
-  using namespace ARDUINOJSON_NAMESPACE;
+  using namespace ArduinoJson::detail;
 
   SECTION("read") {
     BoundedReader<const __FlashStringHelper*> reader(F("\x01\xFF"), 2);
@@ -133,7 +133,7 @@ TEST_CASE("BoundedReader<const __FlashStringHelper*>") {
 }
 
 TEST_CASE("Reader<const __FlashStringHelper*>") {
-  using namespace ARDUINOJSON_NAMESPACE;
+  using namespace ArduinoJson::detail;
 
   SECTION("read()") {
     Reader<const __FlashStringHelper*> reader(F("\x01\xFF\x00\x12"));
diff --git a/neosensor/libraries/ArduinoJson/extras/tests/MixedConfiguration/enable_string_deduplication_0.cpp b/neosensor/libraries/ArduinoJson/extras/tests/MixedConfiguration/enable_string_deduplication_0.cpp
index d9ca166e..7277af94 100644
--- a/neosensor/libraries/ArduinoJson/extras/tests/MixedConfiguration/enable_string_deduplication_0.cpp
+++ b/neosensor/libraries/ArduinoJson/extras/tests/MixedConfiguration/enable_string_deduplication_0.cpp
@@ -1,5 +1,5 @@
 // ArduinoJson - https://arduinojson.org
-// Copyright © 2014-2022, Benoit BLANCHON
+// Copyright © 2014-2023, Benoit BLANCHON
 // MIT License
 
 #define ARDUINOJSON_ENABLE_ARDUINO_STRING 1
diff --git a/neosensor/libraries/ArduinoJson/extras/tests/MixedConfiguration/enable_string_deduplication_1.cpp b/neosensor/libraries/ArduinoJson/extras/tests/MixedConfiguration/enable_string_deduplication_1.cpp
index 27fc18d6..d3b2285e 100644
--- a/neosensor/libraries/ArduinoJson/extras/tests/MixedConfiguration/enable_string_deduplication_1.cpp
+++ b/neosensor/libraries/ArduinoJson/extras/tests/MixedConfiguration/enable_string_deduplication_1.cpp
@@ -1,5 +1,5 @@
 // ArduinoJson - https://arduinojson.org
-// Copyright © 2014-2022, Benoit BLANCHON
+// Copyright © 2014-2023, Benoit BLANCHON
 // MIT License
 
 #define ARDUINOJSON_ENABLE_ARDUINO_STRING 1
diff --git a/neosensor/libraries/ArduinoJson/extras/tests/MixedConfiguration/issue1707.cpp b/neosensor/libraries/ArduinoJson/extras/tests/MixedConfiguration/issue1707.cpp
index befdee53..f8ca566b 100644
--- a/neosensor/libraries/ArduinoJson/extras/tests/MixedConfiguration/issue1707.cpp
+++ b/neosensor/libraries/ArduinoJson/extras/tests/MixedConfiguration/issue1707.cpp
@@ -1,5 +1,5 @@
 // ArduinoJson - https://arduinojson.org
-// Copyright © 2014-2022, Benoit BLANCHON
+// Copyright © 2014-2023, Benoit BLANCHON
 // MIT License
 
 #define ARDUINO
diff --git a/neosensor/libraries/ArduinoJson/extras/tests/Cpp11/use_long_long_0.cpp b/neosensor/libraries/ArduinoJson/extras/tests/MixedConfiguration/use_long_long_0.cpp
similarity index 100%
rename from neosensor/libraries/ArduinoJson/extras/tests/Cpp11/use_long_long_0.cpp
rename to neosensor/libraries/ArduinoJson/extras/tests/MixedConfiguration/use_long_long_0.cpp
diff --git a/neosensor/libraries/ArduinoJson/extras/tests/Cpp11/use_long_long_1.cpp b/neosensor/libraries/ArduinoJson/extras/tests/MixedConfiguration/use_long_long_1.cpp
similarity index 100%
rename from neosensor/libraries/ArduinoJson/extras/tests/Cpp11/use_long_long_1.cpp
rename to neosensor/libraries/ArduinoJson/extras/tests/MixedConfiguration/use_long_long_1.cpp
diff --git a/neosensor/libraries/ArduinoJson/extras/tests/MsgPackDeserializer/CMakeLists.txt b/neosensor/libraries/ArduinoJson/extras/tests/MsgPackDeserializer/CMakeLists.txt
index f2435279..a5442076 100644
--- a/neosensor/libraries/ArduinoJson/extras/tests/MsgPackDeserializer/CMakeLists.txt
+++ b/neosensor/libraries/ArduinoJson/extras/tests/MsgPackDeserializer/CMakeLists.txt
@@ -1,5 +1,5 @@
 # ArduinoJson - https://arduinojson.org
-# Copyright © 2014-2022, Benoit BLANCHON
+# Copyright © 2014-2023, Benoit BLANCHON
 # MIT License
 
 add_executable(MsgPackDeserializerTests
@@ -20,5 +20,5 @@ add_test(MsgPackDeserializer MsgPackDeserializerTests)
 
 set_tests_properties(MsgPackDeserializer
 	PROPERTIES
-		LABELS 		"Catch"
+		LABELS "Catch"
 )
diff --git a/neosensor/libraries/ArduinoJson/extras/tests/MsgPackDeserializer/deserializeArray.cpp b/neosensor/libraries/ArduinoJson/extras/tests/MsgPackDeserializer/deserializeArray.cpp
index 4540bd05..356a9fe4 100644
--- a/neosensor/libraries/ArduinoJson/extras/tests/MsgPackDeserializer/deserializeArray.cpp
+++ b/neosensor/libraries/ArduinoJson/extras/tests/MsgPackDeserializer/deserializeArray.cpp
@@ -1,5 +1,5 @@
 // ArduinoJson - https://arduinojson.org
-// Copyright © 2014-2022, Benoit BLANCHON
+// Copyright © 2014-2023, Benoit BLANCHON
 // MIT License
 
 #include <ArduinoJson.h>
diff --git a/neosensor/libraries/ArduinoJson/extras/tests/MsgPackDeserializer/deserializeObject.cpp b/neosensor/libraries/ArduinoJson/extras/tests/MsgPackDeserializer/deserializeObject.cpp
index 7a0e7b84..4aeb5532 100644
--- a/neosensor/libraries/ArduinoJson/extras/tests/MsgPackDeserializer/deserializeObject.cpp
+++ b/neosensor/libraries/ArduinoJson/extras/tests/MsgPackDeserializer/deserializeObject.cpp
@@ -1,5 +1,5 @@
 // ArduinoJson - https://arduinojson.org
-// Copyright © 2014-2022, Benoit BLANCHON
+// Copyright © 2014-2023, Benoit BLANCHON
 // MIT License
 
 #include <ArduinoJson.h>
diff --git a/neosensor/libraries/ArduinoJson/extras/tests/MsgPackDeserializer/deserializeStaticVariant.cpp b/neosensor/libraries/ArduinoJson/extras/tests/MsgPackDeserializer/deserializeStaticVariant.cpp
index 2d4093ca..0e126ff2 100644
--- a/neosensor/libraries/ArduinoJson/extras/tests/MsgPackDeserializer/deserializeStaticVariant.cpp
+++ b/neosensor/libraries/ArduinoJson/extras/tests/MsgPackDeserializer/deserializeStaticVariant.cpp
@@ -1,5 +1,5 @@
 // ArduinoJson - https://arduinojson.org
-// Copyright © 2014-2022, Benoit BLANCHON
+// Copyright © 2014-2023, Benoit BLANCHON
 // MIT License
 
 #include <ArduinoJson.h>
diff --git a/neosensor/libraries/ArduinoJson/extras/tests/MsgPackDeserializer/deserializeVariant.cpp b/neosensor/libraries/ArduinoJson/extras/tests/MsgPackDeserializer/deserializeVariant.cpp
index be20b9ca..87f38d6e 100644
--- a/neosensor/libraries/ArduinoJson/extras/tests/MsgPackDeserializer/deserializeVariant.cpp
+++ b/neosensor/libraries/ArduinoJson/extras/tests/MsgPackDeserializer/deserializeVariant.cpp
@@ -1,5 +1,5 @@
 // ArduinoJson - https://arduinojson.org
-// Copyright © 2014-2022, Benoit BLANCHON
+// Copyright © 2014-2023, Benoit BLANCHON
 // MIT License
 
 #include <ArduinoJson.h>
diff --git a/neosensor/libraries/ArduinoJson/extras/tests/MsgPackDeserializer/doubleToFloat.cpp b/neosensor/libraries/ArduinoJson/extras/tests/MsgPackDeserializer/doubleToFloat.cpp
index b0470de5..fb2c25d0 100644
--- a/neosensor/libraries/ArduinoJson/extras/tests/MsgPackDeserializer/doubleToFloat.cpp
+++ b/neosensor/libraries/ArduinoJson/extras/tests/MsgPackDeserializer/doubleToFloat.cpp
@@ -1,11 +1,11 @@
 // ArduinoJson - https://arduinojson.org
-// Copyright © 2014-2022, Benoit BLANCHON
+// Copyright © 2014-2023, Benoit BLANCHON
 // MIT License
 
 #include <ArduinoJson.h>
 #include <catch.hpp>
 
-using namespace ARDUINOJSON_NAMESPACE;
+using namespace ArduinoJson::detail;
 
 template <typename T>
 static void check(const char* input, T expected) {
diff --git a/neosensor/libraries/ArduinoJson/extras/tests/MsgPackDeserializer/filter.cpp b/neosensor/libraries/ArduinoJson/extras/tests/MsgPackDeserializer/filter.cpp
index 57dd7bea..78700db6 100644
--- a/neosensor/libraries/ArduinoJson/extras/tests/MsgPackDeserializer/filter.cpp
+++ b/neosensor/libraries/ArduinoJson/extras/tests/MsgPackDeserializer/filter.cpp
@@ -1,11 +1,13 @@
 // ArduinoJson - https://arduinojson.org
-// Copyright © 2014-2022, Benoit BLANCHON
+// Copyright © 2014-2023, Benoit BLANCHON
 // MIT License
 
 #include <ArduinoJson.h>
 #include <catch.hpp>
 
-using namespace ARDUINOJSON_NAMESPACE;
+#include <sstream>
+
+using namespace ArduinoJson::detail;
 
 TEST_CASE("deserializeMsgPack() filter") {
   StaticJsonDocument<4096> doc;
diff --git a/neosensor/libraries/ArduinoJson/extras/tests/MsgPackDeserializer/incompleteInput.cpp b/neosensor/libraries/ArduinoJson/extras/tests/MsgPackDeserializer/incompleteInput.cpp
index 029facfc..6a563882 100644
--- a/neosensor/libraries/ArduinoJson/extras/tests/MsgPackDeserializer/incompleteInput.cpp
+++ b/neosensor/libraries/ArduinoJson/extras/tests/MsgPackDeserializer/incompleteInput.cpp
@@ -1,5 +1,5 @@
 // ArduinoJson - https://arduinojson.org
-// Copyright © 2014-2022, Benoit BLANCHON
+// Copyright © 2014-2023, Benoit BLANCHON
 // MIT License
 
 #include <ArduinoJson.h>
diff --git a/neosensor/libraries/ArduinoJson/extras/tests/MsgPackDeserializer/input_types.cpp b/neosensor/libraries/ArduinoJson/extras/tests/MsgPackDeserializer/input_types.cpp
index f46d4691..783aaa2c 100644
--- a/neosensor/libraries/ArduinoJson/extras/tests/MsgPackDeserializer/input_types.cpp
+++ b/neosensor/libraries/ArduinoJson/extras/tests/MsgPackDeserializer/input_types.cpp
@@ -1,5 +1,5 @@
 // ArduinoJson - https://arduinojson.org
-// Copyright © 2014-2022, Benoit BLANCHON
+// Copyright © 2014-2023, Benoit BLANCHON
 // MIT License
 
 #include <ArduinoJson.h>
diff --git a/neosensor/libraries/ArduinoJson/extras/tests/MsgPackDeserializer/misc.cpp b/neosensor/libraries/ArduinoJson/extras/tests/MsgPackDeserializer/misc.cpp
index 07171716..25137b35 100644
--- a/neosensor/libraries/ArduinoJson/extras/tests/MsgPackDeserializer/misc.cpp
+++ b/neosensor/libraries/ArduinoJson/extras/tests/MsgPackDeserializer/misc.cpp
@@ -1,10 +1,12 @@
 // ArduinoJson - https://arduinojson.org
-// Copyright © 2014-2022, Benoit BLANCHON
+// Copyright © 2014-2023, Benoit BLANCHON
 // MIT License
 
 #include <ArduinoJson.h>
 #include <catch.hpp>
 
+#include <sstream>
+
 TEST_CASE("deserializeMsgPack() returns EmptyInput") {
   StaticJsonDocument<100> doc;
 
diff --git a/neosensor/libraries/ArduinoJson/extras/tests/MsgPackDeserializer/nestingLimit.cpp b/neosensor/libraries/ArduinoJson/extras/tests/MsgPackDeserializer/nestingLimit.cpp
index 6005f55b..2936415a 100644
--- a/neosensor/libraries/ArduinoJson/extras/tests/MsgPackDeserializer/nestingLimit.cpp
+++ b/neosensor/libraries/ArduinoJson/extras/tests/MsgPackDeserializer/nestingLimit.cpp
@@ -1,10 +1,12 @@
 // ArduinoJson - https://arduinojson.org
-// Copyright © 2014-2022, Benoit BLANCHON
+// Copyright © 2014-2023, Benoit BLANCHON
 // MIT License
 
 #include <ArduinoJson.h>
 #include <catch.hpp>
 
+#include <sstream>
+
 #define SHOULD_WORK(expression) REQUIRE(DeserializationError::Ok == expression);
 #define SHOULD_FAIL(expression) \
   REQUIRE(DeserializationError::TooDeep == expression);
diff --git a/neosensor/libraries/ArduinoJson/extras/tests/MsgPackDeserializer/notSupported.cpp b/neosensor/libraries/ArduinoJson/extras/tests/MsgPackDeserializer/notSupported.cpp
index 5e204cdf..2be0f1ec 100644
--- a/neosensor/libraries/ArduinoJson/extras/tests/MsgPackDeserializer/notSupported.cpp
+++ b/neosensor/libraries/ArduinoJson/extras/tests/MsgPackDeserializer/notSupported.cpp
@@ -1,5 +1,5 @@
 // ArduinoJson - https://arduinojson.org
-// Copyright © 2014-2022, Benoit BLANCHON
+// Copyright © 2014-2023, Benoit BLANCHON
 // MIT License
 
 #include <ArduinoJson.h>
diff --git a/neosensor/libraries/ArduinoJson/extras/tests/MsgPackSerializer/CMakeLists.txt b/neosensor/libraries/ArduinoJson/extras/tests/MsgPackSerializer/CMakeLists.txt
index 9a7721e9..2f3d8cc7 100644
--- a/neosensor/libraries/ArduinoJson/extras/tests/MsgPackSerializer/CMakeLists.txt
+++ b/neosensor/libraries/ArduinoJson/extras/tests/MsgPackSerializer/CMakeLists.txt
@@ -1,5 +1,5 @@
 # ArduinoJson - https://arduinojson.org
-# Copyright © 2014-2022, Benoit BLANCHON
+# Copyright © 2014-2023, Benoit BLANCHON
 # MIT License
 
 add_executable(MsgPackSerializerTests
@@ -15,5 +15,5 @@ add_test(MsgPackSerializer MsgPackSerializerTests)
 
 set_tests_properties(MsgPackSerializer
 	PROPERTIES
-		LABELS 		"Catch"
+		LABELS "Catch"
 )
diff --git a/neosensor/libraries/ArduinoJson/extras/tests/MsgPackSerializer/destination_types.cpp b/neosensor/libraries/ArduinoJson/extras/tests/MsgPackSerializer/destination_types.cpp
index 6a5bc849..8b0dffa0 100644
--- a/neosensor/libraries/ArduinoJson/extras/tests/MsgPackSerializer/destination_types.cpp
+++ b/neosensor/libraries/ArduinoJson/extras/tests/MsgPackSerializer/destination_types.cpp
@@ -1,5 +1,5 @@
 // ArduinoJson - https://arduinojson.org
-// Copyright © 2014-2022, Benoit BLANCHON
+// Copyright © 2014-2023, Benoit BLANCHON
 // MIT License
 
 #include <ArduinoJson.h>
diff --git a/neosensor/libraries/ArduinoJson/extras/tests/MsgPackSerializer/measure.cpp b/neosensor/libraries/ArduinoJson/extras/tests/MsgPackSerializer/measure.cpp
index d49bcd9b..a6a4fc9d 100644
--- a/neosensor/libraries/ArduinoJson/extras/tests/MsgPackSerializer/measure.cpp
+++ b/neosensor/libraries/ArduinoJson/extras/tests/MsgPackSerializer/measure.cpp
@@ -1,5 +1,5 @@
 // ArduinoJson - https://arduinojson.org
-// Copyright © 2014-2022, Benoit BLANCHON
+// Copyright © 2014-2023, Benoit BLANCHON
 // MIT License
 
 #include <ArduinoJson.h>
diff --git a/neosensor/libraries/ArduinoJson/extras/tests/MsgPackSerializer/serializeArray.cpp b/neosensor/libraries/ArduinoJson/extras/tests/MsgPackSerializer/serializeArray.cpp
index 58eae9cc..a8ec639c 100644
--- a/neosensor/libraries/ArduinoJson/extras/tests/MsgPackSerializer/serializeArray.cpp
+++ b/neosensor/libraries/ArduinoJson/extras/tests/MsgPackSerializer/serializeArray.cpp
@@ -1,5 +1,5 @@
 // ArduinoJson - https://arduinojson.org
-// Copyright © 2014-2022, Benoit BLANCHON
+// Copyright © 2014-2023, Benoit BLANCHON
 // MIT License
 
 #include <ArduinoJson.h>
diff --git a/neosensor/libraries/ArduinoJson/extras/tests/MsgPackSerializer/serializeObject.cpp b/neosensor/libraries/ArduinoJson/extras/tests/MsgPackSerializer/serializeObject.cpp
index e5c167a6..88173074 100644
--- a/neosensor/libraries/ArduinoJson/extras/tests/MsgPackSerializer/serializeObject.cpp
+++ b/neosensor/libraries/ArduinoJson/extras/tests/MsgPackSerializer/serializeObject.cpp
@@ -1,5 +1,5 @@
 // ArduinoJson - https://arduinojson.org
-// Copyright © 2014-2022, Benoit BLANCHON
+// Copyright © 2014-2023, Benoit BLANCHON
 // MIT License
 
 #include <ArduinoJson.h>
diff --git a/neosensor/libraries/ArduinoJson/extras/tests/MsgPackSerializer/serializeVariant.cpp b/neosensor/libraries/ArduinoJson/extras/tests/MsgPackSerializer/serializeVariant.cpp
index c0919590..b8a021f9 100644
--- a/neosensor/libraries/ArduinoJson/extras/tests/MsgPackSerializer/serializeVariant.cpp
+++ b/neosensor/libraries/ArduinoJson/extras/tests/MsgPackSerializer/serializeVariant.cpp
@@ -1,5 +1,5 @@
 // ArduinoJson - https://arduinojson.org
-// Copyright © 2014-2022, Benoit BLANCHON
+// Copyright © 2014-2023, Benoit BLANCHON
 // MIT License
 
 #include <ArduinoJson.h>
diff --git a/neosensor/libraries/ArduinoJson/extras/tests/Numbers/CMakeLists.txt b/neosensor/libraries/ArduinoJson/extras/tests/Numbers/CMakeLists.txt
index 05e1e894..48cf8c0f 100644
--- a/neosensor/libraries/ArduinoJson/extras/tests/Numbers/CMakeLists.txt
+++ b/neosensor/libraries/ArduinoJson/extras/tests/Numbers/CMakeLists.txt
@@ -1,10 +1,7 @@
 # ArduinoJson - https://arduinojson.org
-# Copyright © 2014-2022, Benoit BLANCHON
+# Copyright © 2014-2023, Benoit BLANCHON
 # MIT License
 
-set(CMAKE_CXX_STANDARD 11)
-set(CMAKE_CXX_STANDARD_REQUIRED OFF)
-
 add_executable(NumbersTests
 	convertNumber.cpp
 	parseFloat.cpp
@@ -13,10 +10,9 @@ add_executable(NumbersTests
 	parseNumber.cpp
 )
 
-
 add_test(Numbers NumbersTests)
 
 set_tests_properties(Numbers
 	PROPERTIES
-		LABELS 		"Catch"
+		LABELS "Catch"
 )
diff --git a/neosensor/libraries/ArduinoJson/extras/tests/Numbers/convertNumber.cpp b/neosensor/libraries/ArduinoJson/extras/tests/Numbers/convertNumber.cpp
index 8c193dc3..ca0cc5e0 100644
--- a/neosensor/libraries/ArduinoJson/extras/tests/Numbers/convertNumber.cpp
+++ b/neosensor/libraries/ArduinoJson/extras/tests/Numbers/convertNumber.cpp
@@ -1,136 +1,131 @@
 // ArduinoJson - https://arduinojson.org
-// Copyright © 2014-2022, Benoit BLANCHON
+// Copyright © 2014-2023, Benoit BLANCHON
 // MIT License
 
 #include <stdint.h>
 #include <ArduinoJson.hpp>
 #include <catch.hpp>
 
-using namespace ARDUINOJSON_NAMESPACE;
+using namespace ArduinoJson::detail;
 
 TEST_CASE("canConvertNumber<TOut, TIn>()") {
   SECTION("int8_t -> int8_t") {
-    CHECK((canConvertNumber<int8_t, int8_t>(0)) == true);
-    CHECK((canConvertNumber<int8_t, int8_t>(127)) == true);
-    CHECK((canConvertNumber<int8_t, int8_t>(-128)) == true);
+    CHECK(canConvertNumber<int8_t, int8_t>(0) == true);
+    CHECK(canConvertNumber<int8_t, int8_t>(127) == true);
+    CHECK(canConvertNumber<int8_t, int8_t>(-128) == true);
   }
 
   SECTION("int8_t -> int16_t") {
-    CHECK((canConvertNumber<int16_t, int8_t>(0)) == true);
-    CHECK((canConvertNumber<int16_t, int8_t>(127)) == true);
-    CHECK((canConvertNumber<int16_t, int8_t>(-128)) == true);
+    CHECK(canConvertNumber<int16_t, int8_t>(0) == true);
+    CHECK(canConvertNumber<int16_t, int8_t>(127) == true);
+    CHECK(canConvertNumber<int16_t, int8_t>(-128) == true);
   }
 
   SECTION("int8_t -> uint8_t") {
-    CHECK((canConvertNumber<uint8_t, int8_t>(0)) == true);
-    CHECK((canConvertNumber<uint8_t, int8_t>(127)) == true);
-    CHECK((canConvertNumber<uint8_t, int8_t>(-128)) == false);
+    CHECK(canConvertNumber<uint8_t, int8_t>(0) == true);
+    CHECK(canConvertNumber<uint8_t, int8_t>(127) == true);
+    CHECK(canConvertNumber<uint8_t, int8_t>(-128) == false);
   }
 
   SECTION("int8_t -> uint16_t") {
-    CHECK((canConvertNumber<uint16_t, int8_t>(0)) == true);
-    CHECK((canConvertNumber<uint16_t, int8_t>(127)) == true);
-    CHECK((canConvertNumber<uint16_t, int8_t>(-128)) == false);
+    CHECK(canConvertNumber<uint16_t, int8_t>(0) == true);
+    CHECK(canConvertNumber<uint16_t, int8_t>(127) == true);
+    CHECK(canConvertNumber<uint16_t, int8_t>(-128) == false);
   }
 
   SECTION("int16_t -> int8_t") {
-    CHECK((canConvertNumber<int8_t, int16_t>(0)) == true);
-    CHECK((canConvertNumber<int8_t, int16_t>(127)) == true);
-    CHECK((canConvertNumber<int8_t, int16_t>(128)) == false);
-    CHECK((canConvertNumber<int8_t, int16_t>(-128)) == true);
-    CHECK((canConvertNumber<int8_t, int16_t>(-129)) == false);
+    CHECK(canConvertNumber<int8_t, int16_t>(0) == true);
+    CHECK(canConvertNumber<int8_t, int16_t>(127) == true);
+    CHECK(canConvertNumber<int8_t, int16_t>(128) == false);
+    CHECK(canConvertNumber<int8_t, int16_t>(-128) == true);
+    CHECK(canConvertNumber<int8_t, int16_t>(-129) == false);
   }
 
   SECTION("int16_t -> uint8_t") {
-    CHECK((canConvertNumber<uint8_t, int16_t>(0)) == true);
-    CHECK((canConvertNumber<uint8_t, int16_t>(255)) == true);
-    CHECK((canConvertNumber<uint8_t, int16_t>(256)) == false);
-    CHECK((canConvertNumber<uint8_t, int16_t>(-1)) == false);
+    CHECK(canConvertNumber<uint8_t, int16_t>(0) == true);
+    CHECK(canConvertNumber<uint8_t, int16_t>(255) == true);
+    CHECK(canConvertNumber<uint8_t, int16_t>(256) == false);
+    CHECK(canConvertNumber<uint8_t, int16_t>(-1) == false);
   }
 
   SECTION("uint8_t -> int8_t") {
-    CHECK((canConvertNumber<int8_t, uint8_t>(0)) == true);
-    CHECK((canConvertNumber<int8_t, uint8_t>(127)) == true);
-    CHECK((canConvertNumber<int8_t, uint8_t>(128)) == false);
-    CHECK((canConvertNumber<int8_t, uint8_t>(255)) == false);
+    CHECK(canConvertNumber<int8_t, uint8_t>(0) == true);
+    CHECK(canConvertNumber<int8_t, uint8_t>(127) == true);
+    CHECK(canConvertNumber<int8_t, uint8_t>(128) == false);
+    CHECK(canConvertNumber<int8_t, uint8_t>(255) == false);
   }
 
   SECTION("uint8_t -> int16_t") {
-    CHECK((canConvertNumber<int16_t, uint8_t>(0)) == true);
-    CHECK((canConvertNumber<int16_t, uint8_t>(127)) == true);
-    CHECK((canConvertNumber<int16_t, uint8_t>(128)) == true);
-    CHECK((canConvertNumber<int16_t, uint8_t>(255)) == true);
+    CHECK(canConvertNumber<int16_t, uint8_t>(0) == true);
+    CHECK(canConvertNumber<int16_t, uint8_t>(127) == true);
+    CHECK(canConvertNumber<int16_t, uint8_t>(128) == true);
+    CHECK(canConvertNumber<int16_t, uint8_t>(255) == true);
   }
 
   SECTION("uint8_t -> uint8_t") {
-    CHECK((canConvertNumber<uint8_t, uint8_t>(0)) == true);
-    CHECK((canConvertNumber<uint8_t, uint8_t>(127)) == true);
-    CHECK((canConvertNumber<uint8_t, uint8_t>(128)) == true);
-    CHECK((canConvertNumber<uint8_t, uint8_t>(255)) == true);
+    CHECK(canConvertNumber<uint8_t, uint8_t>(0) == true);
+    CHECK(canConvertNumber<uint8_t, uint8_t>(127) == true);
+    CHECK(canConvertNumber<uint8_t, uint8_t>(128) == true);
+    CHECK(canConvertNumber<uint8_t, uint8_t>(255) == true);
   }
 
   SECTION("uint8_t -> uint16_t") {
-    CHECK((canConvertNumber<uint16_t, uint8_t>(0)) == true);
-    CHECK((canConvertNumber<uint16_t, uint8_t>(127)) == true);
-    CHECK((canConvertNumber<uint16_t, uint8_t>(128)) == true);
-    CHECK((canConvertNumber<uint16_t, uint8_t>(255)) == true);
+    CHECK(canConvertNumber<uint16_t, uint8_t>(0) == true);
+    CHECK(canConvertNumber<uint16_t, uint8_t>(127) == true);
+    CHECK(canConvertNumber<uint16_t, uint8_t>(128) == true);
+    CHECK(canConvertNumber<uint16_t, uint8_t>(255) == true);
   }
 
   SECTION("float -> int32_t") {
-    CHECK((canConvertNumber<int32_t, float>(0)) == true);
-    CHECK((canConvertNumber<int32_t, float>(-2.147483904e9f)) == false);
-    CHECK((canConvertNumber<int32_t, float>(-2.147483648e+9f)) == true);
-    CHECK((canConvertNumber<int32_t, float>(2.14748352e+9f)) == true);
-    CHECK((canConvertNumber<int32_t, float>(2.14748365e+9f)) == false);
+    CHECK(canConvertNumber<int32_t, float>(0) == true);
+    CHECK(canConvertNumber<int32_t, float>(-2.147483904e9f) == false);
+    CHECK(canConvertNumber<int32_t, float>(-2.147483648e+9f) == true);
+    CHECK(canConvertNumber<int32_t, float>(2.14748352e+9f) == true);
+    CHECK(canConvertNumber<int32_t, float>(2.14748365e+9f) == false);
   }
 
   SECTION("double -> int32_t") {
-    CHECK((canConvertNumber<int32_t, double>(0)) == true);
-    CHECK((canConvertNumber<int32_t, double>(-2.147483649e+9)) == false);
-    CHECK((canConvertNumber<int32_t, double>(-2.147483648e+9)) == true);
-    CHECK((canConvertNumber<int32_t, double>(2.147483647e+9)) == true);
-    CHECK((canConvertNumber<int32_t, double>(2.147483648e+9)) == false);
+    CHECK(canConvertNumber<int32_t, double>(0) == true);
+    CHECK(canConvertNumber<int32_t, double>(-2.147483649e+9) == false);
+    CHECK(canConvertNumber<int32_t, double>(-2.147483648e+9) == true);
+    CHECK(canConvertNumber<int32_t, double>(2.147483647e+9) == true);
+    CHECK(canConvertNumber<int32_t, double>(2.147483648e+9) == false);
   }
 
   SECTION("float -> uint32_t") {
-    CHECK((canConvertNumber<uint32_t, float>(0)) == true);
-    CHECK((canConvertNumber<uint32_t, float>(-1.401298e-45f)) == false);
-    CHECK((canConvertNumber<uint32_t, float>(4.29496704e+9f)) == true);
-    CHECK((canConvertNumber<uint32_t, float>(4.294967296e+9f)) == false);
+    CHECK(canConvertNumber<uint32_t, float>(0) == true);
+    CHECK(canConvertNumber<uint32_t, float>(-1.401298e-45f) == false);
+    CHECK(canConvertNumber<uint32_t, float>(4.29496704e+9f) == true);
+    CHECK(canConvertNumber<uint32_t, float>(4.294967296e+9f) == false);
   }
 
-#if ARDUINOJSON_HAS_LONG_LONG
   SECTION("float -> int64_t") {
-    CHECK((canConvertNumber<int64_t, float>(0)) == true);
-    CHECK((canConvertNumber<int64_t, float>(-9.22337204e+18f)) == true);
-    CHECK((canConvertNumber<int64_t, float>(9.22337149e+18f)) == true);
-    CHECK((canConvertNumber<int64_t, float>(9.22337204e+18f)) == false);
+    CHECK(canConvertNumber<int64_t, float>(0) == true);
+    CHECK(canConvertNumber<int64_t, float>(-9.22337204e+18f) == true);
+    CHECK(canConvertNumber<int64_t, float>(9.22337149e+18f) == true);
+    CHECK(canConvertNumber<int64_t, float>(9.22337204e+18f) == false);
   }
 
   SECTION("double -> int64_t") {
-    CHECK((canConvertNumber<int64_t, double>(0)) == true);
-    CHECK((canConvertNumber<int64_t, double>(-9.2233720368547758e+18)) == true);
-    CHECK((canConvertNumber<int64_t, double>(9.2233720368547748e+18)) == true);
-    CHECK((canConvertNumber<int64_t, double>(9.2233720368547758e+18)) == false);
+    CHECK(canConvertNumber<int64_t, double>(0) == true);
+    CHECK(canConvertNumber<int64_t, double>(-9.2233720368547758e+18) == true);
+    CHECK(canConvertNumber<int64_t, double>(9.2233720368547748e+18) == true);
+    CHECK(canConvertNumber<int64_t, double>(9.2233720368547758e+18) == false);
   }
 
   SECTION("float -> uint64_t") {
-    CHECK((canConvertNumber<uint64_t, float>(0)) == true);
-    CHECK((canConvertNumber<uint64_t, float>(-1.401298e-45f)) == false);
-    CHECK((canConvertNumber<uint64_t, float>(1.844674297419792384e+19f)) ==
-          true);
-    CHECK((canConvertNumber<uint64_t, float>(1.8446744073709551616e+19f)) ==
+    CHECK(canConvertNumber<uint64_t, float>(0) == true);
+    CHECK(canConvertNumber<uint64_t, float>(-1.401298e-45f) == false);
+    CHECK(canConvertNumber<uint64_t, float>(1.84467429741979238e+19f) == true);
+    CHECK(canConvertNumber<uint64_t, float>(1.844674407370955161e+19f) ==
           false);
   }
 
   SECTION("double -> uint64_t") {
-    CHECK((canConvertNumber<uint64_t, double>(0)) == true);
-    CHECK((canConvertNumber<uint64_t, double>(-4.94065645841247e-324)) ==
-          false);
-    CHECK((canConvertNumber<uint64_t, double>(1.8446744073709549568e+19)) ==
-          true);
-    CHECK((canConvertNumber<uint64_t, double>(1.8446744073709551616e+19)) ==
+    CHECK(canConvertNumber<uint64_t, double>(0) == true);
+    CHECK(canConvertNumber<uint64_t, double>(-4.9406564584124e-324) == false);
+    CHECK(canConvertNumber<uint64_t, double>(1.844674407370954958e+19) == true);
+    CHECK(canConvertNumber<uint64_t, double>(1.844674407370955166e+19) ==
           false);
   }
-#endif
 }
diff --git a/neosensor/libraries/ArduinoJson/extras/tests/Numbers/parseDouble.cpp b/neosensor/libraries/ArduinoJson/extras/tests/Numbers/parseDouble.cpp
index 1ce9a02e..301aa0ce 100644
--- a/neosensor/libraries/ArduinoJson/extras/tests/Numbers/parseDouble.cpp
+++ b/neosensor/libraries/ArduinoJson/extras/tests/Numbers/parseDouble.cpp
@@ -1,5 +1,5 @@
 // ArduinoJson - https://arduinojson.org
-// Copyright © 2014-2022, Benoit BLANCHON
+// Copyright © 2014-2023, Benoit BLANCHON
 // MIT License
 
 #define ARDUINOJSON_USE_DOUBLE 1
@@ -9,7 +9,7 @@
 #include <ArduinoJson.hpp>
 #include <catch.hpp>
 
-using namespace ARDUINOJSON_NAMESPACE;
+using namespace ArduinoJson::detail;
 
 void checkDouble(const char* input, double expected) {
   CAPTURE(input);
diff --git a/neosensor/libraries/ArduinoJson/extras/tests/Numbers/parseFloat.cpp b/neosensor/libraries/ArduinoJson/extras/tests/Numbers/parseFloat.cpp
index ec69cab9..4e9c1d47 100644
--- a/neosensor/libraries/ArduinoJson/extras/tests/Numbers/parseFloat.cpp
+++ b/neosensor/libraries/ArduinoJson/extras/tests/Numbers/parseFloat.cpp
@@ -1,5 +1,5 @@
 // ArduinoJson - https://arduinojson.org
-// Copyright © 2014-2022, Benoit BLANCHON
+// Copyright © 2014-2023, Benoit BLANCHON
 // MIT License
 
 #define ARDUINOJSON_USE_DOUBLE 0
@@ -9,7 +9,7 @@
 #include <ArduinoJson.hpp>
 #include <catch.hpp>
 
-using namespace ARDUINOJSON_NAMESPACE;
+using namespace ArduinoJson::detail;
 
 void checkFloat(const char* input, float expected) {
   CAPTURE(input);
diff --git a/neosensor/libraries/ArduinoJson/extras/tests/Numbers/parseInteger.cpp b/neosensor/libraries/ArduinoJson/extras/tests/Numbers/parseInteger.cpp
index aae66e80..880da764 100644
--- a/neosensor/libraries/ArduinoJson/extras/tests/Numbers/parseInteger.cpp
+++ b/neosensor/libraries/ArduinoJson/extras/tests/Numbers/parseInteger.cpp
@@ -1,12 +1,12 @@
 // ArduinoJson - https://arduinojson.org
-// Copyright © 2014-2022, Benoit BLANCHON
+// Copyright © 2014-2023, Benoit BLANCHON
 // MIT License
 
 #include <stdint.h>
 #include <ArduinoJson.hpp>
 #include <catch.hpp>
 
-using namespace ARDUINOJSON_NAMESPACE;
+using namespace ArduinoJson::detail;
 
 template <typename T>
 void checkInteger(const char* input, T expected) {
diff --git a/neosensor/libraries/ArduinoJson/extras/tests/Numbers/parseNumber.cpp b/neosensor/libraries/ArduinoJson/extras/tests/Numbers/parseNumber.cpp
index 7dd5aa06..48163c15 100644
--- a/neosensor/libraries/ArduinoJson/extras/tests/Numbers/parseNumber.cpp
+++ b/neosensor/libraries/ArduinoJson/extras/tests/Numbers/parseNumber.cpp
@@ -1,16 +1,15 @@
 // ArduinoJson - https://arduinojson.org
-// Copyright © 2014-2022, Benoit BLANCHON
+// Copyright © 2014-2023, Benoit BLANCHON
 // MIT License
 
 #include <ArduinoJson.hpp>
 #include <catch.hpp>
 
-using namespace ARDUINOJSON_NAMESPACE;
+using namespace ArduinoJson;
+using namespace ArduinoJson::detail;
 
 TEST_CASE("Test unsigned integer overflow") {
   VariantData first, second;
-  first.init();
-  second.init();
 
   // Avoids MSVC warning C4127 (conditional expression is constant)
   size_t integerSize = sizeof(JsonInteger);
@@ -29,8 +28,6 @@ TEST_CASE("Test unsigned integer overflow") {
 
 TEST_CASE("Test signed integer overflow") {
   VariantData first, second;
-  first.init();
-  second.init();
 
   // Avoids MSVC warning C4127 (conditional expression is constant)
   size_t integerSize = sizeof(JsonInteger);
@@ -49,7 +46,6 @@ TEST_CASE("Test signed integer overflow") {
 
 TEST_CASE("Invalid value") {
   VariantData result;
-  result.init();
 
   parseNumber("6a3", result);
 
diff --git a/neosensor/libraries/ArduinoJson/extras/tests/TextFormatter/CMakeLists.txt b/neosensor/libraries/ArduinoJson/extras/tests/TextFormatter/CMakeLists.txt
index 57a3f85b..2bc4a899 100644
--- a/neosensor/libraries/ArduinoJson/extras/tests/TextFormatter/CMakeLists.txt
+++ b/neosensor/libraries/ArduinoJson/extras/tests/TextFormatter/CMakeLists.txt
@@ -1,8 +1,8 @@
 # ArduinoJson - https://arduinojson.org
-# Copyright © 2014-2022, Benoit BLANCHON
+# Copyright © 2014-2023, Benoit BLANCHON
 # MIT License
 
-add_executable(TextFormatterTests 
+add_executable(TextFormatterTests
 	writeFloat.cpp
 	writeInteger.cpp
 	writeString.cpp
@@ -14,5 +14,5 @@ add_test(TextFormatter TextFormatterTests)
 
 set_tests_properties(TextFormatter
 	PROPERTIES
-		LABELS 		"Catch"
+		LABELS "Catch"
 )
diff --git a/neosensor/libraries/ArduinoJson/extras/tests/TextFormatter/writeFloat.cpp b/neosensor/libraries/ArduinoJson/extras/tests/TextFormatter/writeFloat.cpp
index 87eef3d9..8e4bd7e7 100644
--- a/neosensor/libraries/ArduinoJson/extras/tests/TextFormatter/writeFloat.cpp
+++ b/neosensor/libraries/ArduinoJson/extras/tests/TextFormatter/writeFloat.cpp
@@ -1,5 +1,5 @@
 // ArduinoJson - https://arduinojson.org
-// Copyright © 2014-2022, Benoit BLANCHON
+// Copyright © 2014-2023, Benoit BLANCHON
 // MIT License
 
 #include <catch.hpp>
@@ -11,13 +11,13 @@
 #include <ArduinoJson/Json/TextFormatter.hpp>
 #include <ArduinoJson/Serialization/Writer.hpp>
 
-using namespace ARDUINOJSON_NAMESPACE;
+using namespace ArduinoJson::detail;
 
 template <typename TFloat>
 void check(TFloat input, const std::string& expected) {
   std::string output;
   Writer<std::string> sb(output);
-  TextFormatter<Writer<std::string> > writer(sb);
+  TextFormatter<Writer<std::string>> writer(sb);
   writer.writeFloat(input);
   REQUIRE(writer.bytesWritten() == output.size());
   CHECK(expected == output);
diff --git a/neosensor/libraries/ArduinoJson/extras/tests/TextFormatter/writeInteger.cpp b/neosensor/libraries/ArduinoJson/extras/tests/TextFormatter/writeInteger.cpp
index cc29fc0f..20cbd29f 100644
--- a/neosensor/libraries/ArduinoJson/extras/tests/TextFormatter/writeInteger.cpp
+++ b/neosensor/libraries/ArduinoJson/extras/tests/TextFormatter/writeInteger.cpp
@@ -1,5 +1,5 @@
 // ArduinoJson - https://arduinojson.org
-// Copyright © 2014-2022, Benoit BLANCHON
+// Copyright © 2014-2023, Benoit BLANCHON
 // MIT License
 
 #include <catch.hpp>
@@ -9,7 +9,7 @@
 #include <ArduinoJson/Json/TextFormatter.hpp>
 #include <ArduinoJson/Serialization/Writer.hpp>
 
-using namespace ARDUINOJSON_NAMESPACE;
+using namespace ArduinoJson::detail;
 
 template <typename T>
 void checkWriteInteger(T value, std::string expected) {
diff --git a/neosensor/libraries/ArduinoJson/extras/tests/TextFormatter/writeString.cpp b/neosensor/libraries/ArduinoJson/extras/tests/TextFormatter/writeString.cpp
index 67b12b50..2be9191c 100644
--- a/neosensor/libraries/ArduinoJson/extras/tests/TextFormatter/writeString.cpp
+++ b/neosensor/libraries/ArduinoJson/extras/tests/TextFormatter/writeString.cpp
@@ -1,5 +1,5 @@
 // ArduinoJson - https://arduinojson.org
-// Copyright © 2014-2022, Benoit BLANCHON
+// Copyright © 2014-2023, Benoit BLANCHON
 // MIT License
 
 #include <catch.hpp>
@@ -7,7 +7,7 @@
 #include <ArduinoJson/Json/TextFormatter.hpp>
 #include <ArduinoJson/Serialization/Writers/StaticStringWriter.hpp>
 
-using namespace ARDUINOJSON_NAMESPACE;
+using namespace ArduinoJson::detail;
 
 void check(const char* input, std::string expected) {
   char output[64] = {0};
diff --git a/neosensor/libraries/ArduinoJson/extras/tests/catch/CMakeLists.txt b/neosensor/libraries/ArduinoJson/extras/tests/catch/CMakeLists.txt
index 776a07a0..c5fb5fb8 100644
--- a/neosensor/libraries/ArduinoJson/extras/tests/catch/CMakeLists.txt
+++ b/neosensor/libraries/ArduinoJson/extras/tests/catch/CMakeLists.txt
@@ -2,7 +2,7 @@
 # Copyright Benoit Blanchon 2014-2021
 # MIT License
 
-set(CMAKE_CXX_STANDARD 11)
+set(CMAKE_CXX_STANDARD 17)
 set(CMAKE_CXX_STANDARD_REQUIRED OFF)
 
 add_library(catch
@@ -12,10 +12,10 @@ add_library(catch
 
 target_include_directories(catch
 	PUBLIC
-	${CMAKE_CURRENT_SOURCE_DIR}
+		${CMAKE_CURRENT_SOURCE_DIR}
 )
 
-if(CMAKE_CXX_COMPILER_ID MATCHES "GNU")
-	# prevent "xxx will change in GCC x.x" with arm-linux-gnueabihf-gcc
-	target_compile_options(catch PRIVATE -Wno-psabi)
+if(MINGW)
+	# prevent "too many sections (32837)" with MinGW
+	target_compile_options(catch PRIVATE -Wa,-mbig-obj)
 endif()
diff --git a/neosensor/libraries/ArduinoJson/extras/tests/catch/catch.hpp b/neosensor/libraries/ArduinoJson/extras/tests/catch/catch.hpp
index fdb046fe..9b309bdd 100644
--- a/neosensor/libraries/ArduinoJson/extras/tests/catch/catch.hpp
+++ b/neosensor/libraries/ArduinoJson/extras/tests/catch/catch.hpp
@@ -1,17 +1,21 @@
 /*
- *  Catch v1.12.2
- *  Generated: 2018-05-14 15:10:01.112442
+ *  Catch v2.13.10
+ *  Generated: 2022-10-16 11:01:23.452308
  *  ----------------------------------------------------------
  *  This file has been merged from multiple headers. Please don't edit it directly
- *  Copyright (c) 2012 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)
  */
 #ifndef TWOBLUECUBES_SINGLE_INCLUDE_CATCH_HPP_INCLUDED
 #define TWOBLUECUBES_SINGLE_INCLUDE_CATCH_HPP_INCLUDED
+// start catch.hpp
 
-#define TWOBLUECUBES_CATCH_HPP_INCLUDED
+
+#define CATCH_VERSION_MAJOR 2
+#define CATCH_VERSION_MINOR 13
+#define CATCH_VERSION_PATCH 10
 
 #ifdef __clang__
 #    pragma clang system_header
@@ -19,36 +23,69 @@
 #    pragma GCC system_header
 #endif
 
-// #included from: internal/catch_suppress_warnings.h
+// start catch_suppress_warnings.h
 
 #ifdef __clang__
 #   ifdef __ICC // icpc defines the __clang__ macro
 #       pragma warning(push)
 #       pragma warning(disable: 161 1682)
 #   else // __ICC
-#       pragma clang diagnostic ignored "-Wglobal-constructors"
-#       pragma clang diagnostic ignored "-Wvariadic-macros"
-#       pragma clang diagnostic ignored "-Wc99-extensions"
-#       pragma clang diagnostic ignored "-Wunused-variable"
 #       pragma clang diagnostic push
 #       pragma clang diagnostic ignored "-Wpadded"
-#       pragma clang diagnostic ignored "-Wc++98-compat"
-#       pragma clang diagnostic ignored "-Wc++98-compat-pedantic"
 #       pragma clang diagnostic ignored "-Wswitch-enum"
 #       pragma clang diagnostic ignored "-Wcovered-switch-default"
 #    endif
 #elif defined __GNUC__
-#    pragma GCC diagnostic ignored "-Wvariadic-macros"
-#    pragma GCC diagnostic ignored "-Wunused-variable"
-#    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"
 #endif
+// end catch_suppress_warnings.h
 #if defined(CATCH_CONFIG_MAIN) || defined(CATCH_CONFIG_RUNNER)
 #  define CATCH_IMPL
+#  define CATCH_CONFIG_ALL_PARTS
+#endif
+
+// In the impl file, we want to have access to all parts of the headers
+// Can also be used to sanely support PCHs
+#if defined(CATCH_CONFIG_ALL_PARTS)
+#  define CATCH_CONFIG_EXTERNAL_INTERFACES
+#  if defined(CATCH_CONFIG_DISABLE_MATCHERS)
+#    undef CATCH_CONFIG_DISABLE_MATCHERS
+#  endif
+#  if !defined(CATCH_CONFIG_ENABLE_CHRONO_STRINGMAKER)
+#    define CATCH_CONFIG_ENABLE_CHRONO_STRINGMAKER
+#  endif
+#endif
+
+#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 (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
+
+#elif defined(WIN32) || defined(__WIN32__) || defined(_WIN32) || defined(_MSC_VER) || defined(__MINGW32__)
+#  define CATCH_PLATFORM_WINDOWS
 #endif
 
+// end catch_platform.h
+
 #ifdef CATCH_IMPL
 #  ifndef CLARA_CONFIG_MAIN
 #    define CLARA_CONFIG_MAIN_NOT_DEFINED
@@ -56,93 +93,106 @@
 #  endif
 #endif
 
-// #included from: internal/catch_notimplemented_exception.h
-#define TWOBLUECUBES_CATCH_NOTIMPLEMENTED_EXCEPTION_H_INCLUDED
+// start catch_user_interfaces.h
 
-// #included from: catch_common.h
-#define TWOBLUECUBES_CATCH_COMMON_H_INCLUDED
+namespace Catch {
+    unsigned int rngSeed();
+}
+
+// end catch_user_interfaces.h
+// start catch_tag_alias_autoregistrar.h
 
-// #included from: catch_compiler_capabilities.h
-#define TWOBLUECUBES_CATCH_COMPILER_CAPABILITIES_HPP_INCLUDED
+// start catch_common.h
 
-// Detect a number of compiler features - mostly C++11/14 conformance - by compiler
+// start catch_compiler_capabilities.h
+
+// Detect a number of compiler features - by compiler
 // The following features are defined:
 //
-// CATCH_CONFIG_CPP11_NULLPTR : is nullptr supported?
-// CATCH_CONFIG_CPP11_NOEXCEPT : is noexcept supported?
-// CATCH_CONFIG_CPP11_GENERATED_METHODS : The delete and default keywords for compiler generated methods
-// CATCH_CONFIG_CPP11_IS_ENUM : std::is_enum is supported?
-// CATCH_CONFIG_CPP11_TUPLE : std::tuple is supported
-// CATCH_CONFIG_CPP11_LONG_LONG : is long long supported?
-// CATCH_CONFIG_CPP11_OVERRIDE : is override supported?
-// CATCH_CONFIG_CPP11_UNIQUE_PTR : is unique_ptr supported (otherwise use auto_ptr)
-// CATCH_CONFIG_CPP11_SHUFFLE : is std::shuffle supported?
-// CATCH_CONFIG_CPP11_TYPE_TRAITS : are type_traits and enable_if supported?
-
-// CATCH_CONFIG_CPP11_OR_GREATER : Is C++11 supported?
-
-// CATCH_CONFIG_VARIADIC_MACROS : are variadic macros supported?
 // CATCH_CONFIG_COUNTER : is the __COUNTER__ macro supported?
 // CATCH_CONFIG_WINDOWS_SEH : is Windows SEH supported?
 // CATCH_CONFIG_POSIX_SIGNALS : are POSIX signals supported?
+// CATCH_CONFIG_DISABLE_EXCEPTIONS : Are exceptions enabled?
 // ****************
 // Note to maintainers: if new toggles are added please document them
 // in configuration.md, too
 // ****************
 
 // In general each macro has a _NO_<feature name> form
-// (e.g. CATCH_CONFIG_CPP11_NO_NULLPTR) which disables the feature.
+// (e.g. CATCH_CONFIG_NO_POSIX_SIGNALS) which disables the feature.
 // Many features, at point of detection, define an _INTERNAL_ macro, so they
 // can be combined, en-mass, with the _NO_ forms later.
 
-// All the C++11 features can be disabled with CATCH_CONFIG_NO_CPP11
-
 #ifdef __cplusplus
 
-#  if __cplusplus >= 201103L
-#    define CATCH_CPP11_OR_GREATER
+#  if (__cplusplus >= 201402L) || (defined(_MSVC_LANG) && _MSVC_LANG >= 201402L)
+#    define CATCH_CPP14_OR_GREATER
 #  endif
 
-#  if __cplusplus >= 201402L
-#    define CATCH_CPP14_OR_GREATER
+#  if (__cplusplus >= 201703L) || (defined(_MSVC_LANG) && _MSVC_LANG >= 201703L)
+#    define CATCH_CPP17_OR_GREATER
 #  endif
 
 #endif
 
-#ifdef __clang__
+// Only GCC compiler should be used in this block, so other compilers trying to
+// mask themselves as GCC should be ignored.
+#if defined(__GNUC__) && !defined(__clang__) && !defined(__ICC) && !defined(__CUDACC__) && !defined(__LCC__)
+#    define CATCH_INTERNAL_START_WARNINGS_SUPPRESSION _Pragma( "GCC diagnostic push" )
+#    define CATCH_INTERNAL_STOP_WARNINGS_SUPPRESSION  _Pragma( "GCC diagnostic pop" )
 
-#  if __has_feature(cxx_nullptr)
-#    define CATCH_INTERNAL_CONFIG_CPP11_NULLPTR
-#  endif
+#    define CATCH_INTERNAL_IGNORE_BUT_WARN(...) (void)__builtin_constant_p(__VA_ARGS__)
+
+#endif
 
-#  if __has_feature(cxx_noexcept)
-#    define CATCH_INTERNAL_CONFIG_CPP11_NOEXCEPT
+#if defined(__clang__)
+
+#    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
 
-#   if defined(CATCH_CPP11_OR_GREATER)
-#       define CATCH_INTERNAL_SUPPRESS_ETD_WARNINGS \
-            _Pragma( "clang diagnostic push" ) \
-            _Pragma( "clang diagnostic ignored \"-Wexit-time-destructors\"" )
-#       define CATCH_INTERNAL_UNSUPPRESS_ETD_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" )
-#   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__
 
 ////////////////////////////////////////////////////////////////////////////////
-// We know some environments not to support full POSIX signals
-#if defined(__CYGWIN__) || defined(__QNX__)
-
-#   if !defined(CATCH_CONFIG_POSIX_SIGNALS)
-#       define CATCH_INTERNAL_CONFIG_NO_POSIX_SIGNALS
-#   endif
+// Assume that non-Windows platforms support posix signals by default
+#if !defined(CATCH_PLATFORM_WINDOWS)
+    #define CATCH_INTERNAL_CONFIG_POSIX_SIGNALS
+#endif
 
+////////////////////////////////////////////////////////////////////////////////
+// We know some environments not to support full POSIX signals
+#if defined(__CYGWIN__) || defined(__QNX__) || defined(__EMSCRIPTEN__) || defined(__DJGPP__)
+    #define CATCH_INTERNAL_CONFIG_NO_POSIX_SIGNALS
 #endif
 
 #ifdef __OS400__
@@ -150,6 +200,25 @@
 #       define CATCH_CONFIG_COLOUR_NONE
 #endif
 
+////////////////////////////////////////////////////////////////////////////////
+// Android somehow still does not support std::to_string
+#if defined(__ANDROID__)
+#    define CATCH_INTERNAL_CONFIG_NO_CPP11_TO_STRING
+#    define CATCH_INTERNAL_CONFIG_ANDROID_LOGWRITE
+#endif
+
+////////////////////////////////////////////////////////////////////////////////
+// Not all Windows environments support SEH properly
+#if defined(__MINGW32__)
+#    define CATCH_INTERNAL_CONFIG_NO_WINDOWS_SEH
+#endif
+
+////////////////////////////////////////////////////////////////////////////////
+// PS4
+#if defined(__ORBIS__)
+#    define CATCH_INTERNAL_CONFIG_NO_NEW_CAPTURE
+#endif
+
 ////////////////////////////////////////////////////////////////////////////////
 // Cygwin
 #ifdef __CYGWIN__
@@ -157,219 +226,245 @@
 // Required for some versions of Cygwin to declare gettimeofday
 // see: http://stackoverflow.com/questions/36901803/gettimeofday-not-declared-in-this-scope-cygwin
 #   define _BSD_SOURCE
+// some versions of cygwin (most) do not support std::to_string. Use the libstd check.
+// https://gcc.gnu.org/onlinedocs/gcc-4.8.2/libstdc++/api/a01053_source.html line 2812-2813
+# if !((__cplusplus >= 201103L) && defined(_GLIBCXX_USE_C99) \
+           && !defined(_GLIBCXX_HAVE_BROKEN_VSWPRINTF))
 
-#endif // __CYGWIN__
-
-////////////////////////////////////////////////////////////////////////////////
-// Borland
-#ifdef __BORLANDC__
+#    define CATCH_INTERNAL_CONFIG_NO_CPP11_TO_STRING
 
-#endif // __BORLANDC__
+# endif
+#endif // __CYGWIN__
 
 ////////////////////////////////////////////////////////////////////////////////
-// EDG
-#ifdef __EDG_VERSION__
-
-#endif // __EDG_VERSION__
+// Visual C++
+#if defined(_MSC_VER)
 
-////////////////////////////////////////////////////////////////////////////////
-// Digital Mars
-#ifdef __DMC__
+// Universal Windows platform does not support SEH
+// Or console colours (or console at all...)
+#  if defined(WINAPI_FAMILY) && (WINAPI_FAMILY == WINAPI_FAMILY_APP)
+#    define CATCH_CONFIG_COLOUR_NONE
+#  else
+#    define CATCH_INTERNAL_CONFIG_WINDOWS_SEH
+#  endif
 
-#endif // __DMC__
+#  if !defined(__clang__) // Handle Clang masquerading for msvc
 
-////////////////////////////////////////////////////////////////////////////////
-// GCC
-#ifdef __GNUC__
+// MSVC traditional preprocessor needs some workaround for __VA_ARGS__
+// _MSVC_TRADITIONAL == 0 means new conformant preprocessor
+// _MSVC_TRADITIONAL == 1 means old traditional non-conformant preprocessor
+#    if !defined(_MSVC_TRADITIONAL) || (defined(_MSVC_TRADITIONAL) && _MSVC_TRADITIONAL)
+#      define CATCH_INTERNAL_CONFIG_TRADITIONAL_MSVC_PREPROCESSOR
+#    endif // MSVC_TRADITIONAL
 
-#   if __GNUC__ == 4 && __GNUC_MINOR__ >= 6 && defined(__GXX_EXPERIMENTAL_CXX0X__)
-#       define CATCH_INTERNAL_CONFIG_CPP11_NULLPTR
-#   endif
+// 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__
 
-// - otherwise more recent versions define __cplusplus >= 201103L
-// and will get picked up below
+#endif // _MSC_VER
 
-#endif // __GNUC__
+#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
 
 ////////////////////////////////////////////////////////////////////////////////
-// Visual C++
-#ifdef _MSC_VER
-
-#define CATCH_INTERNAL_CONFIG_WINDOWS_SEH
-
-#if (_MSC_VER >= 1600)
-#   define CATCH_INTERNAL_CONFIG_CPP11_NULLPTR
-#   define CATCH_INTERNAL_CONFIG_CPP11_UNIQUE_PTR
+// Check if we are compiled with -fno-exceptions or equivalent
+#if defined(__EXCEPTIONS) || defined(__cpp_exceptions) || defined(_CPPUNWIND)
+#  define CATCH_INTERNAL_CONFIG_EXCEPTIONS_ENABLED
 #endif
 
-#if (_MSC_VER >= 1900 ) // (VC++ 13 (VS2015))
-#define CATCH_INTERNAL_CONFIG_CPP11_NOEXCEPT
-#define CATCH_INTERNAL_CONFIG_CPP11_GENERATED_METHODS
-#define CATCH_INTERNAL_CONFIG_CPP11_SHUFFLE
-#define CATCH_INTERNAL_CONFIG_CPP11_TYPE_TRAITS
-#endif
-
-#endif // _MSC_VER
-
 ////////////////////////////////////////////////////////////////////////////////
+// DJGPP
+#ifdef __DJGPP__
+#  define CATCH_INTERNAL_CONFIG_NO_WCHAR
+#endif // __DJGPP__
 
-// Use variadic macros if the compiler supports them
-#if ( defined _MSC_VER && _MSC_VER > 1400 && !defined __EDGE__) || \
-    ( defined __WAVE__ && __WAVE_HAS_VARIADICS ) || \
-    ( defined __GNUC__ && __GNUC__ >= 3 ) || \
-    ( !defined __cplusplus && __STDC_VERSION__ >= 199901L || __cplusplus >= 201103L )
-
-#define CATCH_INTERNAL_CONFIG_VARIADIC_MACROS
-
+////////////////////////////////////////////////////////////////////////////////
+// Embarcadero C++Build
+#if defined(__BORLANDC__)
+    #define CATCH_INTERNAL_CONFIG_POLYFILL_ISNAN
 #endif
 
-// Use __COUNTER__ if the compiler supports it
-#if ( defined _MSC_VER && _MSC_VER >= 1300 ) || \
-    ( defined __GNUC__  && ( __GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 3 )) ) || \
-    ( defined __clang__ && __clang_major__ >= 3 )
+////////////////////////////////////////////////////////////////////////////////
 
-// Use of __COUNTER__ is suppressed during code analysis in CLion/AppCode 2017.2.x and former,
-// because __COUNTER__ is not properly handled by it.
-// This does not affect compilation
-#if ( !defined __JETBRAINS_IDE__ || __JETBRAINS_IDE__ >= 20170300L )
+// Use of __COUNTER__ is suppressed during code analysis in
+// CLion/AppCode 2017.2.x and former, because __COUNTER__ is not properly
+// handled by it.
+// Otherwise all supported compilers support COUNTER macro,
+// but user still might want to turn it off
+#if ( !defined(__JETBRAINS_IDE__) || __JETBRAINS_IDE__ >= 20170300L )
     #define CATCH_INTERNAL_CONFIG_COUNTER
 #endif
 
-#endif
-
 ////////////////////////////////////////////////////////////////////////////////
-// C++ language feature support
 
-// catch all support for C++11
-#if defined(CATCH_CPP11_OR_GREATER)
-
-#  if !defined(CATCH_INTERNAL_CONFIG_CPP11_NULLPTR)
-#    define CATCH_INTERNAL_CONFIG_CPP11_NULLPTR
-#  endif
+// 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
 
-#  ifndef CATCH_INTERNAL_CONFIG_CPP11_NOEXCEPT
-#    define CATCH_INTERNAL_CONFIG_CPP11_NOEXCEPT
-#  endif
+#if !defined(_GLIBCXX_USE_C99_MATH_TR1)
+#define CATCH_INTERNAL_CONFIG_GLOBAL_NEXTAFTER
+#endif
 
-#  ifndef CATCH_INTERNAL_CONFIG_CPP11_GENERATED_METHODS
-#    define CATCH_INTERNAL_CONFIG_CPP11_GENERATED_METHODS
-#  endif
+// Various stdlib support checks that require __has_include
+#if defined(__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)
 
-#  ifndef CATCH_INTERNAL_CONFIG_CPP11_IS_ENUM
-#    define CATCH_INTERNAL_CONFIG_CPP11_IS_ENUM
-#  endif
+#if defined(CATCH_INTERNAL_CONFIG_COUNTER) && !defined(CATCH_CONFIG_NO_COUNTER) && !defined(CATCH_CONFIG_COUNTER)
+#   define CATCH_CONFIG_COUNTER
+#endif
+#if defined(CATCH_INTERNAL_CONFIG_WINDOWS_SEH) && !defined(CATCH_CONFIG_NO_WINDOWS_SEH) && !defined(CATCH_CONFIG_WINDOWS_SEH) && !defined(CATCH_INTERNAL_CONFIG_NO_WINDOWS_SEH)
+#   define CATCH_CONFIG_WINDOWS_SEH
+#endif
+// This is set by default, because we assume that unix compilers are posix-signal-compatible by default.
+#if defined(CATCH_INTERNAL_CONFIG_POSIX_SIGNALS) && !defined(CATCH_INTERNAL_CONFIG_NO_POSIX_SIGNALS) && !defined(CATCH_CONFIG_NO_POSIX_SIGNALS) && !defined(CATCH_CONFIG_POSIX_SIGNALS)
+#   define CATCH_CONFIG_POSIX_SIGNALS
+#endif
+// This is set by default, because we assume that compilers with no wchar_t support are just rare exceptions.
+#if !defined(CATCH_INTERNAL_CONFIG_NO_WCHAR) && !defined(CATCH_CONFIG_NO_WCHAR) && !defined(CATCH_CONFIG_WCHAR)
+#   define CATCH_CONFIG_WCHAR
+#endif
 
-#  ifndef CATCH_INTERNAL_CONFIG_CPP11_TUPLE
-#    define CATCH_INTERNAL_CONFIG_CPP11_TUPLE
-#  endif
+#if !defined(CATCH_INTERNAL_CONFIG_NO_CPP11_TO_STRING) && !defined(CATCH_CONFIG_NO_CPP11_TO_STRING) && !defined(CATCH_CONFIG_CPP11_TO_STRING)
+#    define CATCH_CONFIG_CPP11_TO_STRING
+#endif
 
-#  ifndef CATCH_INTERNAL_CONFIG_VARIADIC_MACROS
-#    define CATCH_INTERNAL_CONFIG_VARIADIC_MACROS
-#  endif
+#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_CPP11_LONG_LONG)
-#    define CATCH_INTERNAL_CONFIG_CPP11_LONG_LONG
-#  endif
+#if defined(CATCH_INTERNAL_CONFIG_CPP17_STRING_VIEW) && !defined(CATCH_CONFIG_NO_CPP17_STRING_VIEW) && !defined(CATCH_CONFIG_CPP17_STRING_VIEW)
+#  define CATCH_CONFIG_CPP17_STRING_VIEW
+#endif
 
-#  if !defined(CATCH_INTERNAL_CONFIG_CPP11_OVERRIDE)
-#    define CATCH_INTERNAL_CONFIG_CPP11_OVERRIDE
-#  endif
-#  if !defined(CATCH_INTERNAL_CONFIG_CPP11_UNIQUE_PTR)
-#    define CATCH_INTERNAL_CONFIG_CPP11_UNIQUE_PTR
-#  endif
-# if !defined(CATCH_INTERNAL_CONFIG_CPP11_SHUFFLE)
-#   define CATCH_INTERNAL_CONFIG_CPP11_SHUFFLE
-#  endif
-# if !defined(CATCH_INTERNAL_CONFIG_CPP11_TYPE_TRAITS)
-#  define CATCH_INTERNAL_CONFIG_CPP11_TYPE_TRAITS
-# endif
+#if defined(CATCH_INTERNAL_CONFIG_CPP17_VARIANT) && !defined(CATCH_CONFIG_NO_CPP17_VARIANT) && !defined(CATCH_CONFIG_CPP17_VARIANT)
+#  define CATCH_CONFIG_CPP17_VARIANT
+#endif
 
-#endif // __cplusplus >= 201103L
+#if defined(CATCH_INTERNAL_CONFIG_CPP17_BYTE) && !defined(CATCH_CONFIG_NO_CPP17_BYTE) && !defined(CATCH_CONFIG_CPP17_BYTE)
+#  define CATCH_CONFIG_CPP17_BYTE
+#endif
 
-// Now set the actual defines based on the above + anything the user has configured
-#if defined(CATCH_INTERNAL_CONFIG_CPP11_NULLPTR) && !defined(CATCH_CONFIG_CPP11_NO_NULLPTR) && !defined(CATCH_CONFIG_CPP11_NULLPTR) && !defined(CATCH_CONFIG_NO_CPP11)
-#   define CATCH_CONFIG_CPP11_NULLPTR
+#if defined(CATCH_CONFIG_EXPERIMENTAL_REDIRECT)
+#  define CATCH_INTERNAL_CONFIG_NEW_CAPTURE
 #endif
-#if defined(CATCH_INTERNAL_CONFIG_CPP11_NOEXCEPT) && !defined(CATCH_CONFIG_CPP11_NO_NOEXCEPT) && !defined(CATCH_CONFIG_CPP11_NOEXCEPT) && !defined(CATCH_CONFIG_NO_CPP11)
-#   define CATCH_CONFIG_CPP11_NOEXCEPT
+
+#if defined(CATCH_INTERNAL_CONFIG_NEW_CAPTURE) && !defined(CATCH_INTERNAL_CONFIG_NO_NEW_CAPTURE) && !defined(CATCH_CONFIG_NO_NEW_CAPTURE) && !defined(CATCH_CONFIG_NEW_CAPTURE)
+#  define CATCH_CONFIG_NEW_CAPTURE
 #endif
-#if defined(CATCH_INTERNAL_CONFIG_CPP11_GENERATED_METHODS) && !defined(CATCH_CONFIG_CPP11_NO_GENERATED_METHODS) && !defined(CATCH_CONFIG_CPP11_GENERATED_METHODS) && !defined(CATCH_CONFIG_NO_CPP11)
-#   define CATCH_CONFIG_CPP11_GENERATED_METHODS
+
+#if !defined(CATCH_INTERNAL_CONFIG_EXCEPTIONS_ENABLED) && !defined(CATCH_CONFIG_DISABLE_EXCEPTIONS)
+#  define CATCH_CONFIG_DISABLE_EXCEPTIONS
 #endif
-#if defined(CATCH_INTERNAL_CONFIG_CPP11_IS_ENUM) && !defined(CATCH_CONFIG_CPP11_NO_IS_ENUM) && !defined(CATCH_CONFIG_CPP11_IS_ENUM) && !defined(CATCH_CONFIG_NO_CPP11)
-#   define CATCH_CONFIG_CPP11_IS_ENUM
+
+#if defined(CATCH_INTERNAL_CONFIG_POLYFILL_ISNAN) && !defined(CATCH_CONFIG_NO_POLYFILL_ISNAN) && !defined(CATCH_CONFIG_POLYFILL_ISNAN)
+#  define CATCH_CONFIG_POLYFILL_ISNAN
 #endif
-#if defined(CATCH_INTERNAL_CONFIG_CPP11_TUPLE) && !defined(CATCH_CONFIG_CPP11_NO_TUPLE) && !defined(CATCH_CONFIG_CPP11_TUPLE) && !defined(CATCH_CONFIG_NO_CPP11)
-#   define CATCH_CONFIG_CPP11_TUPLE
+
+#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_VARIADIC_MACROS) && !defined(CATCH_CONFIG_NO_VARIADIC_MACROS) && !defined(CATCH_CONFIG_VARIADIC_MACROS)
-#   define CATCH_CONFIG_VARIADIC_MACROS
+
+#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_CPP11_LONG_LONG) && !defined(CATCH_CONFIG_CPP11_NO_LONG_LONG) && !defined(CATCH_CONFIG_CPP11_LONG_LONG) && !defined(CATCH_CONFIG_NO_CPP11)
-#   define CATCH_CONFIG_CPP11_LONG_LONG
+
+#if defined(CATCH_INTERNAL_CONFIG_GLOBAL_NEXTAFTER) && !defined(CATCH_CONFIG_NO_GLOBAL_NEXTAFTER) && !defined(CATCH_CONFIG_GLOBAL_NEXTAFTER)
+#  define CATCH_CONFIG_GLOBAL_NEXTAFTER
 #endif
-#if defined(CATCH_INTERNAL_CONFIG_CPP11_OVERRIDE) && !defined(CATCH_CONFIG_CPP11_NO_OVERRIDE) && !defined(CATCH_CONFIG_CPP11_OVERRIDE) && !defined(CATCH_CONFIG_NO_CPP11)
-#   define CATCH_CONFIG_CPP11_OVERRIDE
+
+// 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_CONFIG_CPP11_UNIQUE_PTR) && !defined(CATCH_CONFIG_CPP11_NO_UNIQUE_PTR) && !defined(CATCH_CONFIG_CPP11_UNIQUE_PTR) && !defined(CATCH_CONFIG_NO_CPP11)
-#   define CATCH_CONFIG_CPP11_UNIQUE_PTR
+#if !defined(CATCH_INTERNAL_STOP_WARNINGS_SUPPRESSION)
+#   define CATCH_INTERNAL_STOP_WARNINGS_SUPPRESSION
 #endif
-#if defined(CATCH_INTERNAL_CONFIG_COUNTER) && !defined(CATCH_CONFIG_NO_COUNTER) && !defined(CATCH_CONFIG_COUNTER)
-#   define CATCH_CONFIG_COUNTER
+#if !defined(CATCH_INTERNAL_SUPPRESS_PARENTHESES_WARNINGS)
+#   define CATCH_INTERNAL_SUPPRESS_PARENTHESES_WARNINGS
 #endif
-#if defined(CATCH_INTERNAL_CONFIG_CPP11_SHUFFLE) && !defined(CATCH_CONFIG_CPP11_NO_SHUFFLE) && !defined(CATCH_CONFIG_CPP11_SHUFFLE) && !defined(CATCH_CONFIG_NO_CPP11)
-#   define CATCH_CONFIG_CPP11_SHUFFLE
+#if !defined(CATCH_INTERNAL_SUPPRESS_GLOBALS_WARNINGS)
+#   define CATCH_INTERNAL_SUPPRESS_GLOBALS_WARNINGS
 #endif
-# if defined(CATCH_INTERNAL_CONFIG_CPP11_TYPE_TRAITS) && !defined(CATCH_CONFIG_CPP11_NO_TYPE_TRAITS) && !defined(CATCH_CONFIG_CPP11_TYPE_TRAITS) && !defined(CATCH_CONFIG_NO_CPP11)
-#  define CATCH_CONFIG_CPP11_TYPE_TRAITS
-# endif
-#if defined(CATCH_INTERNAL_CONFIG_WINDOWS_SEH) && !defined(CATCH_CONFIG_NO_WINDOWS_SEH) && !defined(CATCH_CONFIG_WINDOWS_SEH)
-#   define CATCH_CONFIG_WINDOWS_SEH
+#if !defined(CATCH_INTERNAL_SUPPRESS_UNUSED_WARNINGS)
+#   define CATCH_INTERNAL_SUPPRESS_UNUSED_WARNINGS
 #endif
-// This is set by default, because we assume that unix compilers are posix-signal-compatible by default.
-#if !defined(CATCH_INTERNAL_CONFIG_NO_POSIX_SIGNALS) && !defined(CATCH_CONFIG_NO_POSIX_SIGNALS) && !defined(CATCH_CONFIG_POSIX_SIGNALS)
-#   define CATCH_CONFIG_POSIX_SIGNALS
+#if !defined(CATCH_INTERNAL_SUPPRESS_ZERO_VARIADIC_WARNINGS)
+#   define CATCH_INTERNAL_SUPPRESS_ZERO_VARIADIC_WARNINGS
 #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_ETD_WARNINGS)
-#   define CATCH_INTERNAL_SUPPRESS_ETD_WARNINGS
-#   define CATCH_INTERNAL_UNSUPPRESS_ETD_WARNINGS
+// 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
 
-// noexcept support:
-#if defined(CATCH_CONFIG_CPP11_NOEXCEPT) && !defined(CATCH_NOEXCEPT)
-#  define CATCH_NOEXCEPT noexcept
-#  define CATCH_NOEXCEPT_IS(x) noexcept(x)
-#else
-#  define CATCH_NOEXCEPT throw()
-#  define CATCH_NOEXCEPT_IS(x)
+#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
 
-// nullptr support
-#ifdef CATCH_CONFIG_CPP11_NULLPTR
-#   define CATCH_NULL nullptr
-#else
-#   define CATCH_NULL NULL
+#if !defined(CATCH_INTERNAL_SUPPRESS_UNUSED_TEMPLATE_WARNINGS)
+#   define CATCH_INTERNAL_SUPPRESS_UNUSED_TEMPLATE_WARNINGS
 #endif
 
-// override support
-#ifdef CATCH_CONFIG_CPP11_OVERRIDE
-#   define CATCH_OVERRIDE override
+#if defined(CATCH_CONFIG_DISABLE_EXCEPTIONS)
+#define CATCH_TRY if ((true))
+#define CATCH_CATCH_ALL if ((false))
+#define CATCH_CATCH_ANON(type) if ((false))
 #else
-#   define CATCH_OVERRIDE
+#define CATCH_TRY try
+#define CATCH_CATCH_ALL catch (...)
+#define CATCH_CATCH_ANON(type) catch (type)
 #endif
 
-// unique_ptr support
-#ifdef CATCH_CONFIG_CPP11_UNIQUE_PTR
-#   define CATCH_AUTO_PTR( T ) std::unique_ptr<T>
-#else
-#   define CATCH_AUTO_PTR( T ) std::auto_ptr<T>
+#if defined(CATCH_INTERNAL_CONFIG_TRADITIONAL_MSVC_PREPROCESSOR) && !defined(CATCH_CONFIG_NO_TRADITIONAL_MSVC_PREPROCESSOR) && !defined(CATCH_CONFIG_TRADITIONAL_MSVC_PREPROCESSOR)
+#define CATCH_CONFIG_TRADITIONAL_MSVC_PREPROCESSOR
 #endif
 
+// end catch_compiler_capabilities.h
 #define INTERNAL_CATCH_UNIQUE_NAME_LINE2( name, line ) name##line
 #define INTERNAL_CATCH_UNIQUE_NAME_LINE( name, line ) INTERNAL_CATCH_UNIQUE_NAME_LINE2( name, line )
 #ifdef CATCH_CONFIG_COUNTER
@@ -378,95 +473,48 @@
 #  define INTERNAL_CATCH_UNIQUE_NAME( name ) INTERNAL_CATCH_UNIQUE_NAME_LINE( name, __LINE__ )
 #endif
 
-#define INTERNAL_CATCH_STRINGIFY2( expr ) #expr
-#define INTERNAL_CATCH_STRINGIFY( expr ) INTERNAL_CATCH_STRINGIFY2( expr )
+#include <iosfwd>
+#include <string>
+#include <cstdint>
 
-#include <sstream>
-#include <algorithm>
+// We need a dummy global operator<< so we can bring it into Catch namespace later
+struct Catch_global_namespace_dummy {};
+std::ostream& operator<<(std::ostream&, Catch_global_namespace_dummy);
 
 namespace Catch {
 
-    struct IConfig;
-
     struct CaseSensitive { enum Choice {
         Yes,
         No
     }; };
 
     class NonCopyable {
-#ifdef CATCH_CONFIG_CPP11_GENERATED_METHODS
         NonCopyable( NonCopyable const& )              = delete;
         NonCopyable( NonCopyable && )                  = delete;
         NonCopyable& operator = ( NonCopyable const& ) = delete;
         NonCopyable& operator = ( NonCopyable && )     = delete;
-#else
-        NonCopyable( NonCopyable const& info );
-        NonCopyable& operator = ( NonCopyable const& );
-#endif
 
     protected:
-        NonCopyable() {}
+        NonCopyable();
         virtual ~NonCopyable();
     };
 
-    class SafeBool {
-    public:
-        typedef void (SafeBool::*type)() const;
-
-        static type makeSafe( bool value ) {
-            return value ? &SafeBool::trueValue : 0;
-        }
-    private:
-        void trueValue() const {}
-    };
-
-    template<typename ContainerT>
-    void deleteAll( ContainerT& container ) {
-        typename ContainerT::const_iterator it = container.begin();
-        typename ContainerT::const_iterator itEnd = container.end();
-        for(; it != itEnd; ++it )
-            delete *it;
-    }
-    template<typename AssociativeContainerT>
-    void deleteAllValues( AssociativeContainerT& container ) {
-        typename AssociativeContainerT::const_iterator it = container.begin();
-        typename AssociativeContainerT::const_iterator itEnd = container.end();
-        for(; it != itEnd; ++it )
-            delete it->second;
-    }
-
-    bool startsWith( std::string const& s, std::string const& prefix );
-    bool startsWith( std::string const& s, char prefix );
-    bool endsWith( std::string const& s, std::string const& suffix );
-    bool endsWith( std::string const& s, char suffix );
-    bool contains( std::string const& s, std::string const& infix );
-    void toLowerInPlace( std::string& s );
-    std::string toLower( std::string const& s );
-    std::string trim( std::string const& str );
-    bool replaceInPlace( std::string& str, std::string const& replaceThis, std::string const& withThis );
-
-    struct pluralise {
-        pluralise( std::size_t count, std::string const& label );
-
-        friend std::ostream& operator << ( std::ostream& os, pluralise const& pluraliser );
+    struct SourceLineInfo {
 
-        std::size_t m_count;
-        std::string m_label;
-    };
+        SourceLineInfo() = delete;
+        SourceLineInfo( char const* _file, std::size_t _line ) noexcept
+        :   file( _file ),
+            line( _line )
+        {}
 
-    struct SourceLineInfo {
+        SourceLineInfo( SourceLineInfo const& other )            = default;
+        SourceLineInfo& operator = ( SourceLineInfo const& )     = default;
+        SourceLineInfo( SourceLineInfo&& )              noexcept = default;
+        SourceLineInfo& operator = ( SourceLineInfo&& ) noexcept = default;
 
-        SourceLineInfo();
-        SourceLineInfo( char const* _file, std::size_t _line );
-#  ifdef CATCH_CONFIG_CPP11_GENERATED_METHODS
-        SourceLineInfo(SourceLineInfo const& other)          = default;
-        SourceLineInfo( SourceLineInfo && )                  = default;
-        SourceLineInfo& operator = ( SourceLineInfo const& ) = default;
-        SourceLineInfo& operator = ( SourceLineInfo && )     = default;
-#  endif
-        bool empty() const;
-        bool operator == ( SourceLineInfo const& other ) const;
-        bool operator < ( SourceLineInfo const& other ) const;
+        bool empty() const noexcept { return file[0] == '\0'; }
+        bool operator == ( SourceLineInfo const& other ) const noexcept;
+        bool operator < ( SourceLineInfo const& other ) const noexcept;
 
         char const* file;
         std::size_t line;
@@ -474,24 +522,17 @@ namespace Catch {
 
     std::ostream& operator << ( std::ostream& os, SourceLineInfo const& info );
 
-    // This is just here to avoid compiler warnings with macro constants and boolean literals
-    inline bool isTrue( bool value ){ return value; }
-    inline bool alwaysTrue() { return true; }
-    inline bool alwaysFalse() { return false; }
-
-    void throwLogicError( std::string const& message, SourceLineInfo const& locationInfo );
-
-    void seedRng( IConfig const& config );
-    unsigned int rngSeed();
+    // Bring in operator<< from global namespace into Catch namespace
+    // This is necessary because the overload of operator<< above makes
+    // lookup stop at namespace Catch
+    using ::operator<<;
 
     // Use this in variadic streaming macros to allow
     //    >> +StreamEndStop
     // as well as
     //    >> stuff +StreamEndStop
     struct StreamEndStop {
-        std::string operator+() {
-            return std::string();
-        }
+        std::string operator+() const;
     };
     template<typename T>
     T const& operator + ( T const& value, StreamEndStop ) {
@@ -499,363 +540,812 @@ namespace Catch {
     }
 }
 
-#define CATCH_INTERNAL_LINEINFO ::Catch::SourceLineInfo( __FILE__, static_cast<std::size_t>( __LINE__ ) )
-#define CATCH_INTERNAL_ERROR( msg ) ::Catch::throwLogicError( msg, CATCH_INTERNAL_LINEINFO );
+#define CATCH_INTERNAL_LINEINFO \
+    ::Catch::SourceLineInfo( __FILE__, static_cast<std::size_t>( __LINE__ ) )
 
+// end catch_common.h
 namespace Catch {
 
-    class NotImplementedException : public std::exception
-    {
-    public:
-        NotImplementedException( SourceLineInfo const& lineInfo );
-
-        virtual ~NotImplementedException() CATCH_NOEXCEPT {}
-
-        virtual const char* what() const CATCH_NOEXCEPT;
-
-    private:
-        std::string m_what;
-        SourceLineInfo m_lineInfo;
+    struct RegistrarForTagAliases {
+        RegistrarForTagAliases( char const* alias, char const* tag, SourceLineInfo const& lineInfo );
     };
 
 } // end namespace Catch
 
-///////////////////////////////////////////////////////////////////////////////
-#define CATCH_NOT_IMPLEMENTED throw Catch::NotImplementedException( CATCH_INTERNAL_LINEINFO )
+#define CATCH_REGISTER_TAG_ALIAS( alias, spec ) \
+    CATCH_INTERNAL_START_WARNINGS_SUPPRESSION \
+    CATCH_INTERNAL_SUPPRESS_GLOBALS_WARNINGS \
+    namespace{ Catch::RegistrarForTagAliases INTERNAL_CATCH_UNIQUE_NAME( AutoRegisterTagAlias )( alias, spec, CATCH_INTERNAL_LINEINFO ); } \
+    CATCH_INTERNAL_STOP_WARNINGS_SUPPRESSION
 
-// #included from: internal/catch_context.h
-#define TWOBLUECUBES_CATCH_CONTEXT_H_INCLUDED
+// end catch_tag_alias_autoregistrar.h
+// start catch_test_registry.h
 
-// #included from: catch_interfaces_generators.h
-#define TWOBLUECUBES_CATCH_INTERFACES_GENERATORS_H_INCLUDED
+// start catch_interfaces_testcase.h
 
-#include <string>
+#include <vector>
 
 namespace Catch {
 
-    struct IGeneratorInfo {
-        virtual ~IGeneratorInfo();
-        virtual bool moveNext() = 0;
-        virtual std::size_t getCurrentIndex() const = 0;
+    class TestSpec;
+
+    struct ITestInvoker {
+        virtual void invoke () const = 0;
+        virtual ~ITestInvoker();
     };
 
-    struct IGeneratorsForTest {
-        virtual ~IGeneratorsForTest();
+    class TestCase;
+    struct IConfig;
 
-        virtual IGeneratorInfo& getGeneratorInfo( std::string const& fileInfo, std::size_t size ) = 0;
-        virtual bool moveNext() = 0;
+    struct ITestCaseRegistry {
+        virtual ~ITestCaseRegistry();
+        virtual std::vector<TestCase> const& getAllTests() const = 0;
+        virtual std::vector<TestCase> const& getAllTestsSorted( IConfig const& config ) const = 0;
     };
 
-    IGeneratorsForTest* createGeneratorsForTest();
+    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 );
 
-} // end namespace Catch
+}
 
-// #included from: catch_ptr.hpp
-#define TWOBLUECUBES_CATCH_PTR_HPP_INCLUDED
+// end catch_interfaces_testcase.h
+// start catch_stringref.h
 
-#ifdef __clang__
-#pragma clang diagnostic push
-#pragma clang diagnostic ignored "-Wpadded"
-#endif
+#include <cstddef>
+#include <string>
+#include <iosfwd>
+#include <cassert>
 
 namespace Catch {
 
-    // An intrusive reference counting smart pointer.
-    // T must implement addRef() and release() methods
-    // typically implementing the IShared interface
-    template<typename T>
-    class Ptr {
+    /// A non-owning string class (similar to the forthcoming std::string_view)
+    /// Note that, because a StringRef may be a substring of another string,
+    /// it may not be null terminated.
+    class StringRef {
     public:
-        Ptr() : m_p( CATCH_NULL ){}
-        Ptr( T* p ) : m_p( p ){
-            if( m_p )
-                m_p->addRef();
-        }
-        Ptr( Ptr const& other ) : m_p( other.m_p ){
-            if( m_p )
-                m_p->addRef();
-        }
-        ~Ptr(){
-            if( m_p )
-                m_p->release();
-        }
-        void reset() {
-            if( m_p )
-                m_p->release();
-            m_p = CATCH_NULL;
-        }
-        Ptr& operator = ( T* p ){
-            Ptr temp( p );
-            swap( temp );
-            return *this;
-        }
-        Ptr& operator = ( Ptr const& other ){
-            Ptr temp( other );
-            swap( temp );
-            return *this;
-        }
-        void swap( Ptr& other ) { std::swap( m_p, other.m_p ); }
-        T* get() const{ return m_p; }
-        T& operator*() const { return *m_p; }
-        T* operator->() const { return m_p; }
-        bool operator !() const { return m_p == CATCH_NULL; }
-        operator SafeBool::type() const { return SafeBool::makeSafe( m_p != CATCH_NULL ); }
+        using size_type = std::size_t;
+        using const_iterator = const char*;
 
     private:
-        T* m_p;
-    };
+        static constexpr char const* const s_empty = "";
 
-    struct IShared : NonCopyable {
-        virtual ~IShared();
-        virtual void addRef() const = 0;
-        virtual void release() const = 0;
-    };
+        char const* m_start = s_empty;
+        size_type m_size = 0;
+
+    public: // construction
+        constexpr StringRef() noexcept = default;
 
-    template<typename T = IShared>
-    struct SharedImpl : T {
+        StringRef( char const* rawChars ) noexcept;
 
-        SharedImpl() : m_rc( 0 ){}
+        constexpr StringRef( char const* rawChars, size_type size ) noexcept
+        :   m_start( rawChars ),
+            m_size( size )
+        {}
+
+        StringRef( std::string const& stdString ) noexcept
+        :   m_start( stdString.c_str() ),
+            m_size( stdString.size() )
+        {}
 
-        virtual void addRef() const {
-            ++m_rc;
+        explicit operator std::string() const {
+            return std::string(m_start, m_size);
         }
-        virtual void release() const {
-            if( --m_rc == 0 )
-                delete this;
+
+    public: // operators
+        auto operator == ( StringRef const& other ) const noexcept -> bool;
+        auto operator != (StringRef const& other) const noexcept -> bool {
+            return !(*this == other);
         }
 
-        mutable unsigned int m_rc;
-    };
+        auto operator[] ( size_type index ) const noexcept -> char {
+            assert(index < m_size);
+            return m_start[index];
+        }
 
-} // end namespace Catch
+    public: // named queries
+        constexpr auto empty() const noexcept -> bool {
+            return m_size == 0;
+        }
+        constexpr auto size() const noexcept -> size_type {
+            return m_size;
+        }
 
-#ifdef __clang__
-#pragma clang diagnostic pop
-#endif
+        // Returns the current start pointer. If the StringRef is not
+        // null-terminated, throws std::domain_exception
+        auto c_str() const -> char const*;
 
-namespace Catch {
+    public: // substrings and searches
+        // Returns a substring of [start, start + length).
+        // If start + length > size(), then the substring is [start, size()).
+        // If start > size(), then the substring is empty.
+        auto substr( size_type start, size_type length ) const noexcept -> StringRef;
 
-    class TestCase;
-    class Stream;
-    struct IResultCapture;
-    struct IRunner;
-    struct IGeneratorsForTest;
-    struct IConfig;
+        // Returns the current start pointer. May not be null-terminated.
+        auto data() const noexcept -> char const*;
 
-    struct IContext
-    {
-        virtual ~IContext();
+        constexpr auto isNullTerminated() const noexcept -> bool {
+            return m_start[m_size] == '\0';
+        }
 
-        virtual IResultCapture* getResultCapture() = 0;
-        virtual IRunner* getRunner() = 0;
-        virtual size_t getGeneratorIndex( std::string const& fileInfo, size_t totalSize ) = 0;
-        virtual bool advanceGeneratorsForCurrentTest() = 0;
-        virtual Ptr<IConfig const> getConfig() const = 0;
+    public: // iterators
+        constexpr const_iterator begin() const { return m_start; }
+        constexpr const_iterator end() const { return m_start + m_size; }
     };
 
-    struct IMutableContext : IContext
-    {
-        virtual ~IMutableContext();
-        virtual void setResultCapture( IResultCapture* resultCapture ) = 0;
-        virtual void setRunner( IRunner* runner ) = 0;
-        virtual void setConfig( Ptr<IConfig const> const& config ) = 0;
-    };
+    auto operator += ( std::string& lhs, StringRef const& sr ) -> std::string&;
+    auto operator << ( std::ostream& os, StringRef const& sr ) -> std::ostream&;
 
-    IContext& getCurrentContext();
-    IMutableContext& getCurrentMutableContext();
-    void cleanUpContext();
-    Stream createStream( std::string const& streamName );
+    constexpr auto operator "" _sr( char const* rawChars, std::size_t size ) noexcept -> StringRef {
+        return StringRef( rawChars, size );
+    }
+} // namespace Catch
 
+constexpr auto operator "" _catch_sr( char const* rawChars, std::size_t size ) noexcept -> Catch::StringRef {
+    return Catch::StringRef( rawChars, size );
 }
 
-// #included from: internal/catch_test_registry.hpp
-#define TWOBLUECUBES_CATCH_TEST_REGISTRY_HPP_INCLUDED
+// end catch_stringref.h
+// start catch_preprocessor.hpp
 
-// #included from: catch_interfaces_testcase.h
-#define TWOBLUECUBES_CATCH_INTERFACES_TESTCASE_H_INCLUDED
 
-#include <vector>
+#define CATCH_RECURSION_LEVEL0(...) __VA_ARGS__
+#define CATCH_RECURSION_LEVEL1(...) CATCH_RECURSION_LEVEL0(CATCH_RECURSION_LEVEL0(CATCH_RECURSION_LEVEL0(__VA_ARGS__)))
+#define CATCH_RECURSION_LEVEL2(...) CATCH_RECURSION_LEVEL1(CATCH_RECURSION_LEVEL1(CATCH_RECURSION_LEVEL1(__VA_ARGS__)))
+#define CATCH_RECURSION_LEVEL3(...) CATCH_RECURSION_LEVEL2(CATCH_RECURSION_LEVEL2(CATCH_RECURSION_LEVEL2(__VA_ARGS__)))
+#define CATCH_RECURSION_LEVEL4(...) CATCH_RECURSION_LEVEL3(CATCH_RECURSION_LEVEL3(CATCH_RECURSION_LEVEL3(__VA_ARGS__)))
+#define CATCH_RECURSION_LEVEL5(...) CATCH_RECURSION_LEVEL4(CATCH_RECURSION_LEVEL4(CATCH_RECURSION_LEVEL4(__VA_ARGS__)))
 
-namespace Catch {
+#ifdef CATCH_CONFIG_TRADITIONAL_MSVC_PREPROCESSOR
+#define INTERNAL_CATCH_EXPAND_VARGS(...) __VA_ARGS__
+// MSVC needs more evaluations
+#define CATCH_RECURSION_LEVEL6(...) CATCH_RECURSION_LEVEL5(CATCH_RECURSION_LEVEL5(CATCH_RECURSION_LEVEL5(__VA_ARGS__)))
+#define CATCH_RECURSE(...)  CATCH_RECURSION_LEVEL6(CATCH_RECURSION_LEVEL6(__VA_ARGS__))
+#else
+#define CATCH_RECURSE(...)  CATCH_RECURSION_LEVEL5(__VA_ARGS__)
+#endif
 
-    class TestSpec;
+#define CATCH_REC_END(...)
+#define CATCH_REC_OUT
+
+#define CATCH_EMPTY()
+#define CATCH_DEFER(id) id CATCH_EMPTY()
+
+#define CATCH_REC_GET_END2() 0, CATCH_REC_END
+#define CATCH_REC_GET_END1(...) CATCH_REC_GET_END2
+#define CATCH_REC_GET_END(...) CATCH_REC_GET_END1
+#define CATCH_REC_NEXT0(test, next, ...) next CATCH_REC_OUT
+#define CATCH_REC_NEXT1(test, next) CATCH_DEFER ( CATCH_REC_NEXT0 ) ( test, next, 0)
+#define CATCH_REC_NEXT(test, next)  CATCH_REC_NEXT1(CATCH_REC_GET_END test, next)
+
+#define CATCH_REC_LIST0(f, x, peek, ...) , f(x) CATCH_DEFER ( CATCH_REC_NEXT(peek, CATCH_REC_LIST1) ) ( f, peek, __VA_ARGS__ )
+#define CATCH_REC_LIST1(f, x, peek, ...) , f(x) CATCH_DEFER ( CATCH_REC_NEXT(peek, CATCH_REC_LIST0) ) ( f, peek, __VA_ARGS__ )
+#define CATCH_REC_LIST2(f, x, peek, ...)   f(x) CATCH_DEFER ( CATCH_REC_NEXT(peek, CATCH_REC_LIST1) ) ( f, peek, __VA_ARGS__ )
+
+#define CATCH_REC_LIST0_UD(f, userdata, x, peek, ...) , f(userdata, x) CATCH_DEFER ( CATCH_REC_NEXT(peek, CATCH_REC_LIST1_UD) ) ( f, userdata, peek, __VA_ARGS__ )
+#define CATCH_REC_LIST1_UD(f, userdata, x, peek, ...) , f(userdata, x) CATCH_DEFER ( CATCH_REC_NEXT(peek, CATCH_REC_LIST0_UD) ) ( f, userdata, peek, __VA_ARGS__ )
+#define CATCH_REC_LIST2_UD(f, userdata, x, peek, ...)   f(userdata, x) CATCH_DEFER ( CATCH_REC_NEXT(peek, CATCH_REC_LIST1_UD) ) ( f, userdata, peek, __VA_ARGS__ )
+
+// Applies the function macro `f` to each of the remaining parameters, inserts commas between the results,
+// and passes userdata as the first parameter to each invocation,
+// e.g. CATCH_REC_LIST_UD(f, x, a, b, c) evaluates to f(x, a), f(x, b), f(x, c)
+#define CATCH_REC_LIST_UD(f, userdata, ...) CATCH_RECURSE(CATCH_REC_LIST2_UD(f, userdata, __VA_ARGS__, ()()(), ()()(), ()()(), 0))
+
+#define CATCH_REC_LIST(f, ...) CATCH_RECURSE(CATCH_REC_LIST2(f, __VA_ARGS__, ()()(), ()()(), ()()(), 0))
+
+#define INTERNAL_CATCH_EXPAND1(param) INTERNAL_CATCH_EXPAND2(param)
+#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
 
-    struct ITestCase : IShared {
-        virtual void invoke () const = 0;
-    protected:
-        virtual ~ITestCase();
-    };
+#define INTERNAL_CATCH_MAKE_NAMESPACE2(...) ns_##__VA_ARGS__
+#define INTERNAL_CATCH_MAKE_NAMESPACE(name) INTERNAL_CATCH_MAKE_NAMESPACE2(name)
 
-    class TestCase;
-    struct IConfig;
+#define INTERNAL_CATCH_REMOVE_PARENS(...) INTERNAL_CATCH_EXPAND1(INTERNAL_CATCH_DEF __VA_ARGS__)
 
-    struct ITestCaseRegistry {
-        virtual ~ITestCaseRegistry();
-        virtual std::vector<TestCase> const& getAllTests() const = 0;
-        virtual std::vector<TestCase> const& getAllTestsSorted( IConfig const& config ) const = 0;
+#ifndef CATCH_CONFIG_TRADITIONAL_MSVC_PREPROCESSOR
+#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
+#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(...);
     };
 
-    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 );
+    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>
-class MethodTestCase : public SharedImpl<ITestCase> {
-
+class TestInvokerAsMethod : public ITestInvoker {
+    void (C::*m_testAsMethod)();
 public:
-    MethodTestCase( void (C::*method)() ) : m_method( method ) {}
+    TestInvokerAsMethod( void (C::*testAsMethod)() ) noexcept : m_testAsMethod( testAsMethod ) {}
 
-    virtual void invoke() const {
+    void invoke() const override {
         C obj;
-        (obj.*m_method)();
+        (obj.*m_testAsMethod)();
     }
-
-private:
-    virtual ~MethodTestCase() {}
-
-    void (C::*m_method)();
 };
 
-typedef void(*TestFunction)();
+auto makeTestInvoker( void(*testAsFunction)() ) noexcept -> ITestInvoker*;
 
-struct NameAndDesc {
-    NameAndDesc( const char* _name = "", const char* _description= "" )
-    : name( _name ), description( _description )
-    {}
+template<typename C>
+auto makeTestInvoker( void (C::*testAsMethod)() ) noexcept -> ITestInvoker* {
+    return new(std::nothrow) TestInvokerAsMethod<C>( testAsMethod );
+}
 
-    const char* name;
-    const char* description;
+struct NameAndTags {
+    NameAndTags( StringRef const& name_ = StringRef(), StringRef const& tags_ = StringRef() ) noexcept;
+    StringRef name;
+    StringRef tags;
 };
 
-void registerTestCase
-    (   ITestCase* testCase,
-        char const* className,
-        NameAndDesc const& nameAndDesc,
-        SourceLineInfo const& lineInfo );
-
-struct AutoReg {
-
-    AutoReg
-        (   TestFunction function,
-            SourceLineInfo const& lineInfo,
-            NameAndDesc const& nameAndDesc );
-
-    template<typename C>
-    AutoReg
-        (   void (C::*method)(),
-            char const* className,
-            NameAndDesc const& nameAndDesc,
-            SourceLineInfo const& lineInfo ) {
+struct AutoReg : NonCopyable {
+    AutoReg( ITestInvoker* invoker, SourceLineInfo const& lineInfo, StringRef const& classOrMethod, NameAndTags const& nameAndTags ) noexcept;
+    ~AutoReg();
+};
 
-        registerTestCase
-            (   new MethodTestCase<C>( method ),
-                className,
-                nameAndDesc,
-                lineInfo );
-    }
+} // end namespace Catch
 
-    ~AutoReg();
+#if defined(CATCH_CONFIG_DISABLE)
+    #define INTERNAL_CATCH_TESTCASE_NO_REGISTRATION( TestName, ... ) \
+        static void TestName()
+    #define INTERNAL_CATCH_TESTCASE_METHOD_NO_REGISTRATION( TestName, ClassName, ... ) \
+        namespace{                        \
+            struct TestName : INTERNAL_CATCH_REMOVE_PARENS(ClassName) { \
+                void test();              \
+            };                            \
+        }                                 \
+        void TestName::test()
+    #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{                                                                                  \
+            namespace INTERNAL_CATCH_MAKE_NAMESPACE(TestName) {                                      \
+            INTERNAL_CATCH_DECLARE_SIG_TEST_METHOD(TestName, ClassName, INTERNAL_CATCH_REMOVE_PARENS(Signature));\
+        }                                                                                           \
+        }                                                                                           \
+        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
 
-private:
-    AutoReg( AutoReg const& );
-    void operator= ( AutoReg const& );
-};
+    #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
 
-void registerTestCaseFunction
-    (   TestFunction function,
-        SourceLineInfo const& lineInfo,
-        NameAndDesc const& nameAndDesc );
+    #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
 
-} // end namespace Catch
+    #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
 
-#ifdef CATCH_CONFIG_VARIADIC_MACROS
     ///////////////////////////////////////////////////////////////////////////////
     #define INTERNAL_CATCH_TESTCASE2( TestName, ... ) \
         static void TestName(); \
-        CATCH_INTERNAL_SUPPRESS_ETD_WARNINGS \
-        namespace{ Catch::AutoReg INTERNAL_CATCH_UNIQUE_NAME( autoRegistrar )( &TestName, CATCH_INTERNAL_LINEINFO, Catch::NameAndDesc( __VA_ARGS__ ) ); } /* NOLINT */ \
-        CATCH_INTERNAL_UNSUPPRESS_ETD_WARNINGS \
+        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_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_SUPPRESS_ETD_WARNINGS \
-        namespace{ Catch::AutoReg INTERNAL_CATCH_UNIQUE_NAME( autoRegistrar )( &QualifiedMethod, "&" #QualifiedMethod, Catch::NameAndDesc( __VA_ARGS__ ), CATCH_INTERNAL_LINEINFO ); } /* NOLINT */ \
-        CATCH_INTERNAL_UNSUPPRESS_ETD_WARNINGS
+        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_STOP_WARNINGS_SUPPRESSION
 
     ///////////////////////////////////////////////////////////////////////////////
     #define INTERNAL_CATCH_TEST_CASE_METHOD2( TestName, ClassName, ... )\
-        CATCH_INTERNAL_SUPPRESS_ETD_WARNINGS \
+        CATCH_INTERNAL_START_WARNINGS_SUPPRESSION \
+        CATCH_INTERNAL_SUPPRESS_GLOBALS_WARNINGS \
         namespace{ \
-            struct TestName : ClassName{ \
+            struct TestName : INTERNAL_CATCH_REMOVE_PARENS(ClassName) { \
                 void test(); \
             }; \
-            Catch::AutoReg INTERNAL_CATCH_UNIQUE_NAME( autoRegistrar ) ( &TestName::test, #ClassName, Catch::NameAndDesc( __VA_ARGS__ ), CATCH_INTERNAL_LINEINFO ); /* NOLINT */ \
+            Catch::AutoReg INTERNAL_CATCH_UNIQUE_NAME( autoRegistrar ) ( Catch::makeTestInvoker( &TestName::test ), CATCH_INTERNAL_LINEINFO, #ClassName, Catch::NameAndTags{ __VA_ARGS__ } ); /* NOLINT */ \
         } \
-        CATCH_INTERNAL_UNSUPPRESS_ETD_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_SUPPRESS_ETD_WARNINGS \
-        Catch::AutoReg( Function, CATCH_INTERNAL_LINEINFO, Catch::NameAndDesc( __VA_ARGS__ ) ); /* NOLINT */ \
-        CATCH_INTERNAL_UNSUPPRESS_ETD_WARNINGS
+        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_STOP_WARNINGS_SUPPRESSION
 
-#else
     ///////////////////////////////////////////////////////////////////////////////
-    #define INTERNAL_CATCH_TESTCASE2( TestName, Name, Desc ) \
-        static void TestName(); \
-        CATCH_INTERNAL_SUPPRESS_ETD_WARNINGS \
-        namespace{ Catch::AutoReg INTERNAL_CATCH_UNIQUE_NAME( autoRegistrar )( &TestName, CATCH_INTERNAL_LINEINFO, Catch::NameAndDesc( Name, Desc ) ); } /* NOLINT */ \
-        CATCH_INTERNAL_UNSUPPRESS_ETD_WARNINGS \
-        static void TestName()
-    #define INTERNAL_CATCH_TESTCASE( Name, Desc ) \
-        INTERNAL_CATCH_TESTCASE2( INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_S_T____ ), Name, Desc )
+    #define INTERNAL_CATCH_TEMPLATE_TEST_CASE_2(TestName, TestFunc, 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 \
+        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{\
+                TestName(){\
+                    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{}, Catch::NameAndTags{ Name " - " + std::string(tmpl_types[index]), Tags } ), index++)... };/* NOLINT */ \
+                }\
+            };\
+            static int INTERNAL_CATCH_UNIQUE_NAME( globalRegistrar ) = [](){\
+            TestName<INTERNAL_CATCH_MAKE_TYPE_LISTS_FROM_TYPES(__VA_ARGS__)>();\
+            return 0;\
+        }();\
+        }\
+        }\
+        CATCH_INTERNAL_STOP_WARNINGS_SUPPRESSION \
+        INTERNAL_CATCH_DEFINE_SIG_TEST(TestFunc,INTERNAL_CATCH_REMOVE_PARENS(Signature))
+
+#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 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
 
-    ///////////////////////////////////////////////////////////////////////////////
-    #define INTERNAL_CATCH_METHOD_AS_TEST_CASE( QualifiedMethod, Name, Desc ) \
-        CATCH_INTERNAL_SUPPRESS_ETD_WARNINGS \
-        namespace{ Catch::AutoReg INTERNAL_CATCH_UNIQUE_NAME( autoRegistrar )( &QualifiedMethod, "&" #QualifiedMethod, Catch::NameAndDesc( Name, Desc ), CATCH_INTERNAL_LINEINFO ); } /* NOLINT */ \
-        CATCH_INTERNAL_UNSUPPRESS_ETD_WARNINGS
+#ifndef CATCH_CONFIG_TRADITIONAL_MSVC_PREPROCESSOR
+    #define INTERNAL_CATCH_TEMPLATE_TEST_CASE_SIG(Name, Tags, Signature, ...) \
+        INTERNAL_CATCH_TEMPLATE_TEST_CASE_2( INTERNAL_CATCH_UNIQUE_NAME( C_A_T_C_H_T_E_M_P_L_A_T_E_T_E_S_T_ ), INTERNAL_CATCH_UNIQUE_NAME( C_A_T_C_H_T_E_M_P_L_A_T_E_T_E_S_T_F_U_N_C_ ), Name, Tags, Signature, __VA_ARGS__ )
+#else
+    #define INTERNAL_CATCH_TEMPLATE_TEST_CASE_SIG(Name, Tags, Signature, ...) \
+        INTERNAL_CATCH_EXPAND_VARGS( INTERNAL_CATCH_TEMPLATE_TEST_CASE_2( INTERNAL_CATCH_UNIQUE_NAME( C_A_T_C_H_T_E_M_P_L_A_T_E_T_E_S_T_ ), INTERNAL_CATCH_UNIQUE_NAME( C_A_T_C_H_T_E_M_P_L_A_T_E_T_E_S_T_F_U_N_C_ ), Name, Tags, Signature, __VA_ARGS__ ) )
+#endif
 
-    ///////////////////////////////////////////////////////////////////////////////
-    #define INTERNAL_CATCH_TEST_CASE_METHOD2( TestCaseName, ClassName, TestName, Desc )\
-        CATCH_INTERNAL_SUPPRESS_ETD_WARNINGS \
-        namespace{ \
-            struct TestCaseName : ClassName{ \
-                void test(); \
-            }; \
-            Catch::AutoReg INTERNAL_CATCH_UNIQUE_NAME( autoRegistrar ) ( &TestCaseName::test, #ClassName, Catch::NameAndDesc( TestName, Desc ), CATCH_INTERNAL_LINEINFO ); /* NOLINT */ \
-        } \
-        CATCH_INTERNAL_UNSUPPRESS_ETD_WARNINGS \
-        void TestCaseName::test()
-    #define INTERNAL_CATCH_TEST_CASE_METHOD( ClassName, TestName, Desc )\
-        INTERNAL_CATCH_TEST_CASE_METHOD2( INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_S_T____ ), ClassName, TestName, Desc )
+    #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_REGISTER_TESTCASE( Function, Name, Desc ) \
-        CATCH_INTERNAL_SUPPRESS_ETD_WARNINGS \
-        Catch::AutoReg( Function, CATCH_INTERNAL_LINEINFO, Catch::NameAndDesc( Name, Desc ) ); /* NOLINT */ \
-        CATCH_INTERNAL_UNSUPPRESS_ETD_WARNINGS
+#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 \
+        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
 
-// #included from: internal/catch_capture.hpp
-#define TWOBLUECUBES_CATCH_CAPTURE_HPP_INCLUDED
+    #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();\
+            };\
+        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{\
+                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( &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 */ \
+                }\
+            };\
+            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_STOP_WARNINGS_SUPPRESSION \
+        template<typename TestType> \
+        void TestName<TestType>::test()
+
+#ifndef CATCH_CONFIG_TRADITIONAL_MSVC_PREPROCESSOR
+    #define INTERNAL_CATCH_TEMPLATE_PRODUCT_TEST_CASE_METHOD( ClassName, Name, Tags, ... )\
+        INTERNAL_CATCH_TEMPLATE_PRODUCT_TEST_CASE_METHOD_2( INTERNAL_CATCH_UNIQUE_NAME( C_A_T_C_H_T_E_M_P_L_A_T_E_T_E_S_T_ ), INTERNAL_CATCH_UNIQUE_NAME( C_A_T_C_H_T_E_M_P_L_A_T_E_T_E_S_T_F_U_N_C_ ), ClassName, Name, Tags, typename T, __VA_ARGS__ )
+#else
+    #define INTERNAL_CATCH_TEMPLATE_PRODUCT_TEST_CASE_METHOD( ClassName, Name, Tags, ... )\
+        INTERNAL_CATCH_EXPAND_VARGS( INTERNAL_CATCH_TEMPLATE_PRODUCT_TEST_CASE_METHOD_2( INTERNAL_CATCH_UNIQUE_NAME( C_A_T_C_H_T_E_M_P_L_A_T_E_T_E_S_T_ ), INTERNAL_CATCH_UNIQUE_NAME( C_A_T_C_H_T_E_M_P_L_A_T_E_T_E_S_T_F_U_N_C_ ), ClassName, Name, Tags, typename T,__VA_ARGS__ ) )
+#endif
 
-// #included from: catch_result_builder.h
-#define TWOBLUECUBES_CATCH_RESULT_BUILDER_H_INCLUDED
+#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
 
-// #included from: catch_result_type.h
-#define TWOBLUECUBES_CATCH_RESULT_TYPE_H_INCLUDED
+    #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
+
+// start catch_assertionhandler.h
+
+// start catch_assertioninfo.h
+
+// start catch_result_type.h
 
 namespace Catch {
 
@@ -880,12 +1370,8 @@ namespace Catch {
 
     }; };
 
-    inline bool isOk( ResultWas::OfType resultType ) {
-        return ( resultType & ResultWas::FailureBit ) == 0;
-    }
-    inline bool isJustInfo( int flags ) {
-        return flags == ResultWas::Info;
-    }
+    bool isOk( ResultWas::OfType resultType );
+    bool isJustInfo( int flags );
 
     // ResultDisposition::Flags enum
     struct ResultDisposition { enum Flags {
@@ -896,622 +1382,125 @@ namespace Catch {
         SuppressFail = 0x08         // Failures are reported but do not fail the test
     }; };
 
-    inline ResultDisposition::Flags operator | ( ResultDisposition::Flags lhs, ResultDisposition::Flags rhs ) {
-        return static_cast<ResultDisposition::Flags>( static_cast<int>( lhs ) | static_cast<int>( rhs ) );
-    }
+    ResultDisposition::Flags operator | ( ResultDisposition::Flags lhs, ResultDisposition::Flags rhs );
 
-    inline bool shouldContinueOnFailure( int flags )    { return ( flags & ResultDisposition::ContinueOnFailure ) != 0; }
-    inline bool isFalseTest( int flags )                { return ( flags & ResultDisposition::FalseTest ) != 0; }
-    inline bool shouldSuppressFailure( int flags )      { return ( flags & ResultDisposition::SuppressFail ) != 0; }
+    bool shouldContinueOnFailure( int flags );
+    inline bool isFalseTest( int flags ) { return ( flags & ResultDisposition::FalseTest ) != 0; }
+    bool shouldSuppressFailure( int flags );
 
 } // end namespace Catch
 
-// #included from: catch_assertionresult.h
-#define TWOBLUECUBES_CATCH_ASSERTIONRESULT_H_INCLUDED
-
-#include <string>
-
+// end catch_result_type.h
 namespace Catch {
 
-    struct STATIC_ASSERT_Expression_Too_Complex_Please_Rewrite_As_Binary_Comparison;
-
-    struct DecomposedExpression
-    {
-        virtual ~DecomposedExpression() {}
-        virtual bool isBinaryExpression() const {
-            return false;
-        }
-        virtual void reconstructExpression( std::string& dest ) const = 0;
-
-        // Only simple binary comparisons can be decomposed.
-        // If more complex check is required then wrap sub-expressions in parentheses.
-        template<typename T> STATIC_ASSERT_Expression_Too_Complex_Please_Rewrite_As_Binary_Comparison& operator + ( T const& );
-        template<typename T> STATIC_ASSERT_Expression_Too_Complex_Please_Rewrite_As_Binary_Comparison& operator - ( T const& );
-        template<typename T> STATIC_ASSERT_Expression_Too_Complex_Please_Rewrite_As_Binary_Comparison& operator * ( T const& );
-        template<typename T> STATIC_ASSERT_Expression_Too_Complex_Please_Rewrite_As_Binary_Comparison& operator / ( T const& );
-        template<typename T> STATIC_ASSERT_Expression_Too_Complex_Please_Rewrite_As_Binary_Comparison& operator % ( T const& );
-        template<typename T> STATIC_ASSERT_Expression_Too_Complex_Please_Rewrite_As_Binary_Comparison& operator && ( T const& );
-        template<typename T> STATIC_ASSERT_Expression_Too_Complex_Please_Rewrite_As_Binary_Comparison& operator || ( T const& );
-
-    private:
-        DecomposedExpression& operator = (DecomposedExpression const&);
-    };
-
     struct AssertionInfo
     {
-        AssertionInfo();
-        AssertionInfo(  char const * _macroName,
-                        SourceLineInfo const& _lineInfo,
-                        char const * _capturedExpression,
-                        ResultDisposition::Flags _resultDisposition,
-                        char const * _secondArg = "");
-
-        char const * macroName;
+        StringRef macroName;
         SourceLineInfo lineInfo;
-        char const * capturedExpression;
+        StringRef capturedExpression;
         ResultDisposition::Flags resultDisposition;
-        char const * secondArg;
-    };
-
-    struct AssertionResultData
-    {
-        AssertionResultData() : decomposedExpression( CATCH_NULL )
-                              , resultType( ResultWas::Unknown )
-                              , negated( false )
-                              , parenthesized( false ) {}
-
-        void negate( bool parenthesize ) {
-            negated = !negated;
-            parenthesized = parenthesize;
-            if( resultType == ResultWas::Ok )
-                resultType = ResultWas::ExpressionFailed;
-            else if( resultType == ResultWas::ExpressionFailed )
-                resultType = ResultWas::Ok;
-        }
-
-        std::string const& reconstructExpression() const {
-            if( decomposedExpression != CATCH_NULL ) {
-                decomposedExpression->reconstructExpression( reconstructedExpression );
-                if( parenthesized ) {
-                    reconstructedExpression.insert( 0, 1, '(' );
-                    reconstructedExpression.append( 1, ')' );
-                }
-                if( negated ) {
-                    reconstructedExpression.insert( 0, 1, '!' );
-                }
-                decomposedExpression = CATCH_NULL;
-            }
-            return reconstructedExpression;
-        }
 
-        mutable DecomposedExpression const* decomposedExpression;
-        mutable std::string reconstructedExpression;
-        std::string message;
-        ResultWas::OfType resultType;
-        bool negated;
-        bool parenthesized;
+        // We want to delete this constructor but a compiler bug in 4.8 means
+        // the struct is then treated as non-aggregate
+        //AssertionInfo() = delete;
     };
 
-    class AssertionResult {
-    public:
-        AssertionResult();
-        AssertionResult( AssertionInfo const& info, AssertionResultData const& data );
-        ~AssertionResult();
-#  ifdef CATCH_CONFIG_CPP11_GENERATED_METHODS
-         AssertionResult( AssertionResult const& )              = default;
-         AssertionResult( AssertionResult && )                  = default;
-         AssertionResult& operator = ( AssertionResult const& ) = default;
-         AssertionResult& operator = ( AssertionResult && )     = default;
-#  endif
+} // end namespace Catch
 
-        bool isOk() const;
-        bool succeeded() const;
-        ResultWas::OfType getResultType() const;
-        bool hasExpression() const;
-        bool hasMessage() const;
-        std::string getExpression() const;
-        std::string getExpressionInMacro() const;
-        bool hasExpandedExpression() const;
-        std::string getExpandedExpression() const;
-        std::string getMessage() const;
-        SourceLineInfo getSourceInfo() const;
-        std::string getTestMacroName() const;
-        void discardDecomposedExpression() const;
-        void expandDecomposedExpression() const;
+// end catch_assertioninfo.h
+// start catch_decomposer.h
 
-    protected:
-        AssertionInfo m_info;
-        AssertionResultData m_resultData;
-    };
+// start catch_tostring.h
 
-} // end namespace Catch
+#include <vector>
+#include <cstddef>
+#include <type_traits>
+#include <string>
+// start catch_stream.h
 
-// #included from: catch_matchers.hpp
-#define TWOBLUECUBES_CATCH_MATCHERS_HPP_INCLUDED
+#include <iosfwd>
+#include <cstddef>
+#include <ostream>
 
 namespace Catch {
-namespace Matchers {
-    namespace Impl {
-
-        template<typename ArgT> struct MatchAllOf;
-        template<typename ArgT> struct MatchAnyOf;
-        template<typename ArgT> struct MatchNotOf;
 
-        class MatcherUntypedBase {
-        public:
-            std::string toString() const {
-                if( m_cachedToString.empty() )
-                    m_cachedToString = describe();
-                return m_cachedToString;
-            }
+    std::ostream& cout();
+    std::ostream& cerr();
+    std::ostream& clog();
 
-        protected:
-            virtual ~MatcherUntypedBase();
-            virtual std::string describe() const = 0;
-            mutable std::string m_cachedToString;
-        private:
-            MatcherUntypedBase& operator = ( MatcherUntypedBase const& );
-        };
+    class StringRef;
 
-        template<typename ObjectT>
-        struct MatcherMethod {
-            virtual bool match( ObjectT const& arg ) const = 0;
-        };
-        template<typename PtrT>
-        struct MatcherMethod<PtrT*> {
-            virtual bool match( PtrT* arg ) const = 0;
-        };
+    struct IStream {
+        virtual ~IStream();
+        virtual std::ostream& stream() const = 0;
+    };
 
-        template<typename ObjectT, typename ComparatorT = ObjectT>
-        struct MatcherBase : MatcherUntypedBase, MatcherMethod<ObjectT> {
+    auto makeStream( StringRef const &filename ) -> IStream const*;
 
-            MatchAllOf<ComparatorT> operator && ( MatcherBase const& other ) const;
-            MatchAnyOf<ComparatorT> operator || ( MatcherBase const& other ) const;
-            MatchNotOf<ComparatorT> operator ! () const;
-        };
+    class ReusableStringStream : NonCopyable {
+        std::size_t m_index;
+        std::ostream* m_oss;
+    public:
+        ReusableStringStream();
+        ~ReusableStringStream();
 
-        template<typename ArgT>
-        struct MatchAllOf : MatcherBase<ArgT> {
-            virtual bool match( ArgT const& arg ) const CATCH_OVERRIDE {
-                for( std::size_t i = 0; i < m_matchers.size(); ++i ) {
-                    if (!m_matchers[i]->match(arg))
-                        return false;
-                }
-                return true;
-            }
-            virtual std::string describe() const CATCH_OVERRIDE {
-                std::string description;
-                description.reserve( 4 + m_matchers.size()*32 );
-                description += "( ";
-                for( std::size_t i = 0; i < m_matchers.size(); ++i ) {
-                    if( i != 0 )
-                        description += " and ";
-                    description += m_matchers[i]->toString();
-                }
-                description += " )";
-                return description;
-            }
+        auto str() const -> std::string;
 
-            MatchAllOf<ArgT>& operator && ( MatcherBase<ArgT> const& other ) {
-                m_matchers.push_back( &other );
-                return *this;
-            }
+        template<typename T>
+        auto operator << ( T const& value ) -> ReusableStringStream& {
+            *m_oss << value;
+            return *this;
+        }
+        auto get() -> std::ostream& { return *m_oss; }
+    };
+}
 
-            std::vector<MatcherBase<ArgT> const*> m_matchers;
-        };
-        template<typename ArgT>
-        struct MatchAnyOf : MatcherBase<ArgT> {
+// end catch_stream.h
+// start catch_interfaces_enum_values_registry.h
 
-            virtual bool match( ArgT const& arg ) const CATCH_OVERRIDE {
-                for( std::size_t i = 0; i < m_matchers.size(); ++i ) {
-                    if (m_matchers[i]->match(arg))
-                        return true;
-                }
-                return false;
-            }
-            virtual std::string describe() const CATCH_OVERRIDE {
-                std::string description;
-                description.reserve( 4 + m_matchers.size()*32 );
-                description += "( ";
-                for( std::size_t i = 0; i < m_matchers.size(); ++i ) {
-                    if( i != 0 )
-                        description += " or ";
-                    description += m_matchers[i]->toString();
-                }
-                description += " )";
-                return description;
-            }
+#include <vector>
 
-            MatchAnyOf<ArgT>& operator || ( MatcherBase<ArgT> const& other ) {
-                m_matchers.push_back( &other );
-                return *this;
-            }
+namespace Catch {
 
-            std::vector<MatcherBase<ArgT> const*> m_matchers;
-        };
+    namespace Detail {
+        struct EnumInfo {
+            StringRef m_name;
+            std::vector<std::pair<int, StringRef>> m_values;
 
-        template<typename ArgT>
-        struct MatchNotOf : MatcherBase<ArgT> {
+            ~EnumInfo();
 
-            MatchNotOf( MatcherBase<ArgT> const& underlyingMatcher ) : m_underlyingMatcher( underlyingMatcher ) {}
+            StringRef lookup( int value ) const;
+        };
+    } // namespace Detail
 
-            virtual bool match( ArgT const& arg ) const CATCH_OVERRIDE {
-                return !m_underlyingMatcher.match( arg );
-            }
+    struct IMutableEnumValuesRegistry {
+        virtual ~IMutableEnumValuesRegistry();
 
-            virtual std::string describe() const CATCH_OVERRIDE {
-                return "not " + m_underlyingMatcher.toString();
-            }
-            MatcherBase<ArgT> const& m_underlyingMatcher;
-        };
+        virtual Detail::EnumInfo const& registerEnum( StringRef enumName, StringRef allEnums, std::vector<int> const& values ) = 0;
 
-        template<typename ObjectT, typename ComparatorT>
-        MatchAllOf<ComparatorT> MatcherBase<ObjectT, ComparatorT>::operator && ( MatcherBase const& other ) const {
-            return MatchAllOf<ComparatorT>() && *this && other;
-        }
-        template<typename ObjectT, typename ComparatorT>
-        MatchAnyOf<ComparatorT> MatcherBase<ObjectT, ComparatorT>::operator || ( MatcherBase const& other ) const {
-            return MatchAnyOf<ComparatorT>() || *this || other;
+        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 );
         }
-        template<typename ObjectT, typename ComparatorT>
-        MatchNotOf<ComparatorT> MatcherBase<ObjectT, ComparatorT>::operator ! () const {
-            return MatchNotOf<ComparatorT>( *this );
-        }
-
-    } // namespace Impl
+    };
 
-    // The following functions create the actual matcher objects.
-    // This allows the types to be inferred
-    // - deprecated: prefer ||, && and !
-    template<typename T>
-    Impl::MatchNotOf<T> Not( Impl::MatcherBase<T> const& underlyingMatcher ) {
-        return Impl::MatchNotOf<T>( underlyingMatcher );
-    }
-    template<typename T>
-    Impl::MatchAllOf<T> AllOf( Impl::MatcherBase<T> const& m1, Impl::MatcherBase<T> const& m2 ) {
-        return Impl::MatchAllOf<T>() && m1 && m2;
-    }
-    template<typename T>
-    Impl::MatchAllOf<T> AllOf( Impl::MatcherBase<T> const& m1, Impl::MatcherBase<T> const& m2, Impl::MatcherBase<T> const& m3 ) {
-        return Impl::MatchAllOf<T>() && m1 && m2 && m3;
-    }
-    template<typename T>
-    Impl::MatchAnyOf<T> AnyOf( Impl::MatcherBase<T> const& m1, Impl::MatcherBase<T> const& m2 ) {
-        return Impl::MatchAnyOf<T>() || m1 || m2;
-    }
-    template<typename T>
-    Impl::MatchAnyOf<T> AnyOf( Impl::MatcherBase<T> const& m1, Impl::MatcherBase<T> const& m2, Impl::MatcherBase<T> const& m3 ) {
-        return Impl::MatchAnyOf<T>() || m1 || m2 || m3;
-    }
+} // Catch
 
-} // namespace Matchers
+// end catch_interfaces_enum_values_registry.h
 
-using namespace Matchers;
-using Matchers::Impl::MatcherBase;
+#ifdef CATCH_CONFIG_CPP17_STRING_VIEW
+#include <string_view>
+#endif
 
-} // namespace Catch
+#ifdef __OBJC__
+// start catch_objc_arc.hpp
 
-namespace Catch {
-
-    struct TestFailureException{};
-
-    template<typename T> class ExpressionLhs;
-
-    struct CopyableStream {
-        CopyableStream() {}
-        CopyableStream( CopyableStream const& other ) {
-            oss << other.oss.str();
-        }
-        CopyableStream& operator=( CopyableStream const& other ) {
-            oss.str(std::string());
-            oss << other.oss.str();
-            return *this;
-        }
-        std::ostringstream oss;
-    };
-
-    class ResultBuilder : public DecomposedExpression {
-    public:
-        ResultBuilder(  char const* macroName,
-                        SourceLineInfo const& lineInfo,
-                        char const* capturedExpression,
-                        ResultDisposition::Flags resultDisposition,
-                        char const* secondArg = "" );
-        ~ResultBuilder();
-
-        template<typename T>
-        ExpressionLhs<T const&> operator <= ( T const& operand );
-        ExpressionLhs<bool> operator <= ( bool value );
-
-        template<typename T>
-        ResultBuilder& operator << ( T const& value ) {
-            stream().oss << value;
-            return *this;
-        }
-
-        ResultBuilder& setResultType( ResultWas::OfType result );
-        ResultBuilder& setResultType( bool result );
-
-        void endExpression( DecomposedExpression const& expr );
-
-        virtual void reconstructExpression( std::string& dest ) const CATCH_OVERRIDE;
-
-        AssertionResult build() const;
-        AssertionResult build( DecomposedExpression const& expr ) const;
-
-        void useActiveException( ResultDisposition::Flags resultDisposition = ResultDisposition::Normal );
-        void captureResult( ResultWas::OfType resultType );
-        void captureExpression();
-        void captureExpectedException( std::string const& expectedMessage );
-        void captureExpectedException( Matchers::Impl::MatcherBase<std::string> const& matcher );
-        void handleResult( AssertionResult const& result );
-        void react();
-        bool shouldDebugBreak() const;
-        bool allowThrows() const;
-
-        template<typename ArgT, typename MatcherT>
-        void captureMatch( ArgT const& arg, MatcherT const& matcher, char const* matcherString );
-
-        void setExceptionGuard();
-        void unsetExceptionGuard();
-
-    private:
-        AssertionInfo m_assertionInfo;
-        AssertionResultData m_data;
-
-        CopyableStream &stream()
-        {
-            if(!m_usedStream)
-            {
-                m_usedStream = true;
-                m_stream().oss.str("");
-            }
-            return m_stream();
-        }
-
-        static CopyableStream &m_stream()
-        {
-            static CopyableStream s;
-            return s;
-        }
-
-        bool m_shouldDebugBreak;
-        bool m_shouldThrow;
-        bool m_guardException;
-        bool m_usedStream;
-    };
-
-} // namespace Catch
-
-// Include after due to circular dependency:
-// #included from: catch_expression_lhs.hpp
-#define TWOBLUECUBES_CATCH_EXPRESSION_LHS_HPP_INCLUDED
-
-// #included from: catch_evaluate.hpp
-#define TWOBLUECUBES_CATCH_EVALUATE_HPP_INCLUDED
-
-#ifdef _MSC_VER
-#pragma warning(push)
-#pragma warning(disable:4389) // '==' : signed/unsigned mismatch
-#pragma warning(disable:4018) // more "signed/unsigned mismatch"
-#pragma warning(disable:4312) // Converting int to T* using reinterpret_cast (issue on x64 platform)
-#endif
-
-#include <cstddef>
-
-namespace Catch {
-namespace Internal {
-
-    enum Operator {
-        IsEqualTo,
-        IsNotEqualTo,
-        IsLessThan,
-        IsGreaterThan,
-        IsLessThanOrEqualTo,
-        IsGreaterThanOrEqualTo
-    };
-
-    template<Operator Op> struct OperatorTraits             { static const char* getName(){ return "*error*"; } };
-    template<> struct OperatorTraits<IsEqualTo>             { static const char* getName(){ return "=="; } };
-    template<> struct OperatorTraits<IsNotEqualTo>          { static const char* getName(){ return "!="; } };
-    template<> struct OperatorTraits<IsLessThan>            { static const char* getName(){ return "<"; } };
-    template<> struct OperatorTraits<IsGreaterThan>         { static const char* getName(){ return ">"; } };
-    template<> struct OperatorTraits<IsLessThanOrEqualTo>   { static const char* getName(){ return "<="; } };
-    template<> struct OperatorTraits<IsGreaterThanOrEqualTo>{ static const char* getName(){ return ">="; } };
-
-    template<typename T>
-    T& opCast(T const& t) { return const_cast<T&>(t); }
-
-// nullptr_t support based on pull request #154 from Konstantin Baumann
-#ifdef CATCH_CONFIG_CPP11_NULLPTR
-    inline std::nullptr_t opCast(std::nullptr_t) { return nullptr; }
-#endif // CATCH_CONFIG_CPP11_NULLPTR
-
-    // So the compare overloads can be operator agnostic we convey the operator as a template
-    // enum, which is used to specialise an Evaluator for doing the comparison.
-    template<typename T1, typename T2, Operator Op>
-    struct Evaluator{};
-
-    template<typename T1, typename T2>
-    struct Evaluator<T1, T2, IsEqualTo> {
-        static bool evaluate( T1 const& lhs, T2 const& rhs) {
-            return bool( opCast( lhs ) ==  opCast( rhs ) );
-        }
-    };
-    template<typename T1, typename T2>
-    struct Evaluator<T1, T2, IsNotEqualTo> {
-        static bool evaluate( T1 const& lhs, T2 const& rhs ) {
-            return bool( opCast( lhs ) != opCast( rhs ) );
-        }
-    };
-    template<typename T1, typename T2>
-    struct Evaluator<T1, T2, IsLessThan> {
-        static bool evaluate( T1 const& lhs, T2 const& rhs ) {
-            return bool( opCast( lhs ) < opCast( rhs ) );
-        }
-    };
-    template<typename T1, typename T2>
-    struct Evaluator<T1, T2, IsGreaterThan> {
-        static bool evaluate( T1 const& lhs, T2 const& rhs ) {
-            return bool( opCast( lhs ) > opCast( rhs ) );
-        }
-    };
-    template<typename T1, typename T2>
-    struct Evaluator<T1, T2, IsGreaterThanOrEqualTo> {
-        static bool evaluate( T1 const& lhs, T2 const& rhs ) {
-            return bool( opCast( lhs ) >= opCast( rhs ) );
-        }
-    };
-    template<typename T1, typename T2>
-    struct Evaluator<T1, T2, IsLessThanOrEqualTo> {
-        static bool evaluate( T1 const& lhs, T2 const& rhs ) {
-            return bool( opCast( lhs ) <= opCast( rhs ) );
-        }
-    };
-
-    template<Operator Op, typename T1, typename T2>
-    bool applyEvaluator( T1 const& lhs, T2 const& rhs ) {
-        return Evaluator<T1, T2, Op>::evaluate( lhs, rhs );
-    }
-
-    // This level of indirection allows us to specialise for integer types
-    // to avoid signed/ unsigned warnings
-
-    // "base" overload
-    template<Operator Op, typename T1, typename T2>
-    bool compare( T1 const& lhs, T2 const& rhs ) {
-        return Evaluator<T1, T2, Op>::evaluate( lhs, rhs );
-    }
-
-    // unsigned X to int
-    template<Operator Op> bool compare( unsigned int lhs, int rhs ) {
-        return applyEvaluator<Op>( lhs, static_cast<unsigned int>( rhs ) );
-    }
-    template<Operator Op> bool compare( unsigned long lhs, int rhs ) {
-        return applyEvaluator<Op>( lhs, static_cast<unsigned int>( rhs ) );
-    }
-    template<Operator Op> bool compare( unsigned char lhs, int rhs ) {
-        return applyEvaluator<Op>( lhs, static_cast<unsigned int>( rhs ) );
-    }
-
-    // unsigned X to long
-    template<Operator Op> bool compare( unsigned int lhs, long rhs ) {
-        return applyEvaluator<Op>( lhs, static_cast<unsigned long>( rhs ) );
-    }
-    template<Operator Op> bool compare( unsigned long lhs, long rhs ) {
-        return applyEvaluator<Op>( lhs, static_cast<unsigned long>( rhs ) );
-    }
-    template<Operator Op> bool compare( unsigned char lhs, long rhs ) {
-        return applyEvaluator<Op>( lhs, static_cast<unsigned long>( rhs ) );
-    }
-
-    // int to unsigned X
-    template<Operator Op> bool compare( int lhs, unsigned int rhs ) {
-        return applyEvaluator<Op>( static_cast<unsigned int>( lhs ), rhs );
-    }
-    template<Operator Op> bool compare( int lhs, unsigned long rhs ) {
-        return applyEvaluator<Op>( static_cast<unsigned int>( lhs ), rhs );
-    }
-    template<Operator Op> bool compare( int lhs, unsigned char rhs ) {
-        return applyEvaluator<Op>( static_cast<unsigned int>( lhs ), rhs );
-    }
-
-    // long to unsigned X
-    template<Operator Op> bool compare( long lhs, unsigned int rhs ) {
-        return applyEvaluator<Op>( static_cast<unsigned long>( lhs ), rhs );
-    }
-    template<Operator Op> bool compare( long lhs, unsigned long rhs ) {
-        return applyEvaluator<Op>( static_cast<unsigned long>( lhs ), rhs );
-    }
-    template<Operator Op> bool compare( long lhs, unsigned char rhs ) {
-        return applyEvaluator<Op>( static_cast<unsigned long>( lhs ), rhs );
-    }
-
-    // pointer to long (when comparing against NULL)
-    template<Operator Op, typename T> bool compare( long lhs, T* rhs ) {
-        return Evaluator<T*, T*, Op>::evaluate( reinterpret_cast<T*>( lhs ), rhs );
-    }
-    template<Operator Op, typename T> bool compare( T* lhs, long rhs ) {
-        return Evaluator<T*, T*, Op>::evaluate( lhs, reinterpret_cast<T*>( rhs ) );
-    }
-
-    // pointer to int (when comparing against NULL)
-    template<Operator Op, typename T> bool compare( int lhs, T* rhs ) {
-        return Evaluator<T*, T*, Op>::evaluate( reinterpret_cast<T*>( lhs ), rhs );
-    }
-    template<Operator Op, typename T> bool compare( T* lhs, int rhs ) {
-        return Evaluator<T*, T*, Op>::evaluate( lhs, reinterpret_cast<T*>( rhs ) );
-    }
-
-#ifdef CATCH_CONFIG_CPP11_LONG_LONG
-    // long long to unsigned X
-    template<Operator Op> bool compare( long long lhs, unsigned int rhs ) {
-        return applyEvaluator<Op>( static_cast<unsigned long>( lhs ), rhs );
-    }
-    template<Operator Op> bool compare( long long lhs, unsigned long rhs ) {
-        return applyEvaluator<Op>( static_cast<unsigned long>( lhs ), rhs );
-    }
-    template<Operator Op> bool compare( long long lhs, unsigned long long rhs ) {
-        return applyEvaluator<Op>( static_cast<unsigned long>( lhs ), rhs );
-    }
-    template<Operator Op> bool compare( long long lhs, unsigned char rhs ) {
-        return applyEvaluator<Op>( static_cast<unsigned long>( lhs ), rhs );
-    }
-
-    // unsigned long long to X
-    template<Operator Op> bool compare( unsigned long long lhs, int rhs ) {
-        return applyEvaluator<Op>( static_cast<long>( lhs ), rhs );
-    }
-    template<Operator Op> bool compare( unsigned long long lhs, long rhs ) {
-        return applyEvaluator<Op>( static_cast<long>( lhs ), rhs );
-    }
-    template<Operator Op> bool compare( unsigned long long lhs, long long rhs ) {
-        return applyEvaluator<Op>( static_cast<long>( lhs ), rhs );
-    }
-    template<Operator Op> bool compare( unsigned long long lhs, char rhs ) {
-        return applyEvaluator<Op>( static_cast<long>( lhs ), rhs );
-    }
-
-    // pointer to long long (when comparing against NULL)
-    template<Operator Op, typename T> bool compare( long long lhs, T* rhs ) {
-        return Evaluator<T*, T*, Op>::evaluate( reinterpret_cast<T*>( lhs ), rhs );
-    }
-    template<Operator Op, typename T> bool compare( T* lhs, long long rhs ) {
-        return Evaluator<T*, T*, Op>::evaluate( lhs, reinterpret_cast<T*>( rhs ) );
-    }
-#endif // CATCH_CONFIG_CPP11_LONG_LONG
-
-#ifdef CATCH_CONFIG_CPP11_NULLPTR
-    // pointer to nullptr_t (when comparing against nullptr)
-    template<Operator Op, typename T> bool compare( std::nullptr_t, T* rhs ) {
-        return Evaluator<T*, T*, Op>::evaluate( nullptr, rhs );
-    }
-    template<Operator Op, typename T> bool compare( T* lhs, std::nullptr_t ) {
-        return Evaluator<T*, T*, Op>::evaluate( lhs, nullptr );
-    }
-#endif // CATCH_CONFIG_CPP11_NULLPTR
-
-} // end of namespace Internal
-} // end of namespace Catch
-
-#ifdef _MSC_VER
-#pragma warning(pop)
-#endif
-
-// #included from: catch_tostring.h
-#define TWOBLUECUBES_CATCH_TOSTRING_H_INCLUDED
-
-#include <sstream>
-#include <iomanip>
-#include <limits>
-#include <vector>
-#include <cstddef>
-
-#ifdef __OBJC__
-// #included from: catch_objc_arc.hpp
-#define TWOBLUECUBES_CATCH_OBJC_ARC_HPP_INCLUDED
-
-#import <Foundation/Foundation.h>
+#import <Foundation/Foundation.h>
 
 #ifdef __has_feature
 #define CATCH_ARC_ENABLED __has_feature(objc_arc)
@@ -1551,883 +1540,1340 @@ inline id performOptionalSelector( id obj, SEL sel ) {
 #define CATCH_ARC_STRONG __strong
 #endif
 
+// end catch_objc_arc.hpp
 #endif
 
-#ifdef CATCH_CONFIG_CPP11_TUPLE
-#include <tuple>
-#endif
-
-#ifdef CATCH_CONFIG_CPP11_IS_ENUM
-#include <type_traits>
+#ifdef _MSC_VER
+#pragma warning(push)
+#pragma warning(disable:4180) // We attempt to stream a function (address) by const&, which MSVC complains about but is harmless
 #endif
 
 namespace Catch {
+    namespace Detail {
 
-// Why we're here.
-template<typename T>
-std::string toString( T const& value );
+        extern const std::string unprintableString;
+
+        std::string rawMemoryToString( const void *object, std::size_t size );
+
+        template<typename T>
+        std::string rawMemoryToString( const T& object ) {
+          return rawMemoryToString( &object, sizeof(object) );
+        }
+
+        template<typename T>
+        class IsStreamInsertable {
+            template<typename Stream, typename U>
+            static auto test(int)
+                -> decltype(std::declval<Stream&>() << std::declval<U>(), std::true_type());
+
+            template<typename, typename>
+            static auto test(...)->std::false_type;
 
-// Built in overloads
+        public:
+            static const bool value = decltype(test<std::ostream, const T&>(0))::value;
+        };
+
+        template<typename E>
+        std::string convertUnknownEnumToString( E e );
+
+        template<typename T>
+        typename std::enable_if<
+            !std::is_enum<T>::value && !std::is_base_of<std::exception, T>::value,
+        std::string>::type convertUnstreamable( T const& ) {
+            return Detail::unprintableString;
+        }
+        template<typename T>
+        typename std::enable_if<
+            !std::is_enum<T>::value && std::is_base_of<std::exception, T>::value,
+         std::string>::type convertUnstreamable(T const& ex) {
+            return ex.what();
+        }
 
-std::string toString( std::string const& value );
-std::string toString( std::wstring const& value );
-std::string toString( const char* const value );
-std::string toString( char* const value );
-std::string toString( const wchar_t* const value );
-std::string toString( wchar_t* const value );
-std::string toString( int value );
-std::string toString( unsigned long value );
-std::string toString( unsigned int value );
-std::string toString( const double value );
-std::string toString( const float value );
-std::string toString( bool value );
-std::string toString( char value );
-std::string toString( signed char value );
-std::string toString( unsigned char value );
+        template<typename T>
+        typename std::enable_if<
+            std::is_enum<T>::value
+        , std::string>::type convertUnstreamable( T const& value ) {
+            return convertUnknownEnumToString( value );
+        }
 
-#ifdef CATCH_CONFIG_CPP11_LONG_LONG
-std::string toString( long long value );
-std::string toString( unsigned long long value );
+#if defined(_MANAGED)
+        //! Convert a CLR string to a utf8 std::string
+        template<typename T>
+        std::string clrReferenceToString( T^ ref ) {
+            if (ref == nullptr)
+                return std::string("null");
+            auto bytes = System::Text::Encoding::UTF8->GetBytes(ref->ToString());
+            cli::pin_ptr<System::Byte> p = &bytes[0];
+            return std::string(reinterpret_cast<char const *>(p), bytes->Length);
+        }
 #endif
 
-#ifdef CATCH_CONFIG_CPP11_NULLPTR
-std::string toString( std::nullptr_t );
+    } // namespace Detail
+
+    // If we decide for C++14, change these to enable_if_ts
+    template <typename T, typename = void>
+    struct StringMaker {
+        template <typename Fake = T>
+        static
+        typename std::enable_if<::Catch::Detail::IsStreamInsertable<Fake>::value, std::string>::type
+            convert(const Fake& value) {
+                ReusableStringStream rss;
+                // NB: call using the function-like syntax to avoid ambiguity with
+                // user-defined templated operator<< under clang.
+                rss.operator<<(value);
+                return rss.str();
+        }
+
+        template <typename Fake = T>
+        static
+        typename std::enable_if<!::Catch::Detail::IsStreamInsertable<Fake>::value, std::string>::type
+            convert( const Fake& value ) {
+#if !defined(CATCH_CONFIG_FALLBACK_STRINGIFIER)
+            return Detail::convertUnstreamable(value);
+#else
+            return CATCH_CONFIG_FALLBACK_STRINGIFIER(value);
 #endif
+        }
+    };
 
-#ifdef __OBJC__
-    std::string toString( NSString const * const& nsstring );
-    std::string toString( NSString * CATCH_ARC_STRONG & nsstring );
-    std::string toString( NSObject* const& nsObject );
+    namespace Detail {
+
+        // This function dispatches all stringification requests inside of Catch.
+        // Should be preferably called fully qualified, like ::Catch::Detail::stringify
+        template <typename T>
+        std::string stringify(const T& e) {
+            return ::Catch::StringMaker<typename std::remove_cv<typename std::remove_reference<T>::type>::type>::convert(e);
+        }
+
+        template<typename E>
+        std::string convertUnknownEnumToString( E e ) {
+            return ::Catch::Detail::stringify(static_cast<typename std::underlying_type<E>::type>(e));
+        }
+
+#if defined(_MANAGED)
+        template <typename T>
+        std::string stringify( T^ e ) {
+            return ::Catch::StringMaker<T^>::convert(e);
+        }
 #endif
 
-namespace Detail {
+    } // namespace Detail
 
-    extern const std::string unprintableString;
+    // Some predefined specializations
 
- #if !defined(CATCH_CONFIG_CPP11_STREAM_INSERTABLE_CHECK)
-    struct BorgType {
-        template<typename T> BorgType( T const& );
+    template<>
+    struct StringMaker<std::string> {
+        static std::string convert(const std::string& str);
     };
 
-    struct TrueType { char sizer[1]; };
-    struct FalseType { char sizer[2]; };
-
-    TrueType& testStreamable( std::ostream& );
-    FalseType testStreamable( FalseType );
+#ifdef CATCH_CONFIG_CPP17_STRING_VIEW
+    template<>
+    struct StringMaker<std::string_view> {
+        static std::string convert(std::string_view str);
+    };
+#endif
 
-    FalseType operator<<( std::ostream const&, BorgType const& );
+    template<>
+    struct StringMaker<char const *> {
+        static std::string convert(char const * str);
+    };
+    template<>
+    struct StringMaker<char *> {
+        static std::string convert(char * str);
+    };
 
-    template<typename T>
-    struct IsStreamInsertable {
-        static std::ostream &s;
-        static T  const&t;
-        enum { value = sizeof( testStreamable(s << t) ) == sizeof( TrueType ) };
+#ifdef CATCH_CONFIG_WCHAR
+    template<>
+    struct StringMaker<std::wstring> {
+        static std::string convert(const std::wstring& wstr);
     };
-#else
-    template<typename T>
-    class IsStreamInsertable {
-        template<typename SS, typename TT>
-        static auto test(int)
-        -> decltype( std::declval<SS&>() << std::declval<TT>(), std::true_type() );
 
-        template<typename, typename>
-        static auto test(...) -> std::false_type;
+# ifdef CATCH_CONFIG_CPP17_STRING_VIEW
+    template<>
+    struct StringMaker<std::wstring_view> {
+        static std::string convert(std::wstring_view str);
+    };
+# endif
 
-    public:
-        static const bool value = decltype(test<std::ostream,const T&>(0))::value;
+    template<>
+    struct StringMaker<wchar_t const *> {
+        static std::string convert(wchar_t const * str);
+    };
+    template<>
+    struct StringMaker<wchar_t *> {
+        static std::string convert(wchar_t * str);
     };
 #endif
 
-#if defined(CATCH_CONFIG_CPP11_IS_ENUM)
-    template<typename T,
-             bool IsEnum = std::is_enum<T>::value
-             >
-    struct EnumStringMaker
-    {
-        static std::string convert( T const& ) { return unprintableString; }
+    // TBD: Should we use `strnlen` to ensure that we don't go out of the buffer,
+    //      while keeping string semantics?
+    template<int SZ>
+    struct StringMaker<char[SZ]> {
+        static std::string convert(char const* str) {
+            return ::Catch::Detail::stringify(std::string{ str });
+        }
     };
-
-    template<typename T>
-    struct EnumStringMaker<T,true>
-    {
-        static std::string convert( T const& v )
-        {
-            return ::Catch::toString(
-                static_cast<typename std::underlying_type<T>::type>(v)
-                );
+    template<int SZ>
+    struct StringMaker<signed char[SZ]> {
+        static std::string convert(signed char const* str) {
+            return ::Catch::Detail::stringify(std::string{ reinterpret_cast<char const *>(str) });
         }
     };
-#endif
-    template<bool C>
-    struct StringMakerBase {
-#if defined(CATCH_CONFIG_CPP11_IS_ENUM)
-        template<typename T>
-        static std::string convert( T const& v )
-        {
-            return EnumStringMaker<T>::convert( v );
+    template<int SZ>
+    struct StringMaker<unsigned char[SZ]> {
+        static std::string convert(unsigned char const* str) {
+            return ::Catch::Detail::stringify(std::string{ reinterpret_cast<char const *>(str) });
         }
-#else
-        template<typename T>
-        static std::string convert( T const& ) { return unprintableString; }
-#endif
     };
 
+#if defined(CATCH_CONFIG_CPP17_BYTE)
     template<>
-    struct StringMakerBase<true> {
-        template<typename T>
-        static std::string convert( T const& _value ) {
-            std::ostringstream oss;
-            oss << _value;
-            return oss.str();
-        }
+    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);
+    };
+    template<>
+    struct StringMaker<long> {
+        static std::string convert(long value);
+    };
+    template<>
+    struct StringMaker<long long> {
+        static std::string convert(long long value);
+    };
+    template<>
+    struct StringMaker<unsigned int> {
+        static std::string convert(unsigned int value);
+    };
+    template<>
+    struct StringMaker<unsigned long> {
+        static std::string convert(unsigned long value);
+    };
+    template<>
+    struct StringMaker<unsigned long long> {
+        static std::string convert(unsigned long long value);
     };
 
-    std::string rawMemoryToString( const void *object, std::size_t size );
+    template<>
+    struct StringMaker<bool> {
+        static std::string convert(bool b);
+    };
 
-    template<typename T>
-    std::string rawMemoryToString( const T& object ) {
-      return rawMemoryToString( &object, sizeof(object) );
-    }
+    template<>
+    struct StringMaker<char> {
+        static std::string convert(char c);
+    };
+    template<>
+    struct StringMaker<signed char> {
+        static std::string convert(signed char c);
+    };
+    template<>
+    struct StringMaker<unsigned char> {
+        static std::string convert(unsigned char c);
+    };
 
-} // end namespace Detail
+    template<>
+    struct StringMaker<std::nullptr_t> {
+        static std::string convert(std::nullptr_t);
+    };
 
-template<typename T>
-struct StringMaker :
-    Detail::StringMakerBase<Detail::IsStreamInsertable<T>::value> {};
+    template<>
+    struct StringMaker<float> {
+        static std::string convert(float value);
+        static int precision;
+    };
 
-template<typename T>
-struct StringMaker<T*> {
-    template<typename U>
-    static std::string convert( U* p ) {
-        if( !p )
-            return "NULL";
-        else
-            return Detail::rawMemoryToString( p );
-    }
-};
+    template<>
+    struct StringMaker<double> {
+        static std::string convert(double value);
+        static int precision;
+    };
 
-template<typename R, typename C>
-struct StringMaker<R C::*> {
-    static std::string convert( R C::* p ) {
-        if( !p )
-            return "NULL";
-        else
-            return Detail::rawMemoryToString( p );
-    }
-};
+    template <typename T>
+    struct StringMaker<T*> {
+        template <typename U>
+        static std::string convert(U* p) {
+            if (p) {
+                return ::Catch::Detail::rawMemoryToString(p);
+            } else {
+                return "nullptr";
+            }
+        }
+    };
 
-namespace Detail {
-    template<typename InputIterator>
-    std::string rangeToString( InputIterator first, InputIterator last );
-}
-
-//template<typename T, typename Allocator>
-//struct StringMaker<std::vector<T, Allocator> > {
-//    static std::string convert( std::vector<T,Allocator> const& v ) {
-//        return Detail::rangeToString( v.begin(), v.end() );
-//    }
-//};
-
-template<typename T, typename Allocator>
-std::string toString( std::vector<T,Allocator> const& v ) {
-    return Detail::rangeToString( v.begin(), v.end() );
-}
-
-#ifdef CATCH_CONFIG_CPP11_TUPLE
-
-// toString for tuples
-namespace TupleDetail {
-  template<
-      typename Tuple,
-      std::size_t N = 0,
-      bool = (N < std::tuple_size<Tuple>::value)
-      >
-  struct ElementPrinter {
-      static void print( const Tuple& tuple, std::ostream& os )
-      {
-          os << ( N ? ", " : " " )
-             << Catch::toString(std::get<N>(tuple));
-          ElementPrinter<Tuple,N+1>::print(tuple,os);
-      }
-  };
+    template <typename R, typename C>
+    struct StringMaker<R C::*> {
+        static std::string convert(R C::* p) {
+            if (p) {
+                return ::Catch::Detail::rawMemoryToString(p);
+            } else {
+                return "nullptr";
+            }
+        }
+    };
 
-  template<
-      typename Tuple,
-      std::size_t N
-      >
-  struct ElementPrinter<Tuple,N,false> {
-      static void print( const Tuple&, std::ostream& ) {}
-  };
+#if defined(_MANAGED)
+    template <typename T>
+    struct StringMaker<T^> {
+        static std::string convert( T^ ref ) {
+            return ::Catch::Detail::clrReferenceToString(ref);
+        }
+    };
+#endif
 
-}
+    namespace Detail {
+        template<typename InputIterator, typename Sentinel = InputIterator>
+        std::string rangeToString(InputIterator first, Sentinel last) {
+            ReusableStringStream rss;
+            rss << "{ ";
+            if (first != last) {
+                rss << ::Catch::Detail::stringify(*first);
+                for (++first; first != last; ++first)
+                    rss << ", " << ::Catch::Detail::stringify(*first);
+            }
+            rss << " }";
+            return rss.str();
+        }
+    }
 
-template<typename ...Types>
-struct StringMaker<std::tuple<Types...>> {
+#ifdef __OBJC__
+    template<>
+    struct StringMaker<NSString*> {
+        static std::string convert(NSString * nsstring) {
+            if (!nsstring)
+                return "nil";
+            return std::string("@") + [nsstring UTF8String];
+        }
+    };
+    template<>
+    struct StringMaker<NSObject*> {
+        static std::string convert(NSObject* nsObject) {
+            return ::Catch::Detail::stringify([nsObject description]);
+        }
 
-    static std::string convert( const std::tuple<Types...>& tuple )
-    {
-        std::ostringstream os;
-        os << '{';
-        TupleDetail::ElementPrinter<std::tuple<Types...>>::print( tuple, os );
-        os << " }";
-        return os.str();
-    }
-};
-#endif // CATCH_CONFIG_CPP11_TUPLE
+    };
+    namespace Detail {
+        inline std::string stringify( NSString* nsstring ) {
+            return StringMaker<NSString*>::convert( nsstring );
+        }
 
-namespace Detail {
-    template<typename T>
-    std::string makeString( T const& value ) {
-        return StringMaker<T>::convert( value );
-    }
-} // end namespace Detail
+    } // namespace Detail
+#endif // __OBJC__
 
-/// \brief converts any type to a string
-///
-/// The default template forwards on to ostringstream - except when an
-/// ostringstream overload does not exist - in which case it attempts to detect
-/// that and writes {?}.
-/// Overload (not specialise) this template for custom typs that you don't want
-/// to provide an ostream overload for.
-template<typename T>
-std::string toString( T const& value ) {
-    return StringMaker<T>::convert( value );
+} // namespace Catch
+
+//////////////////////////////////////////////////////
+// Separate std-lib types stringification, so it can be selectively enabled
+// This means that we do not bring in
+
+#if defined(CATCH_CONFIG_ENABLE_ALL_STRINGMAKERS)
+#  define CATCH_CONFIG_ENABLE_PAIR_STRINGMAKER
+#  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
+#if defined(CATCH_CONFIG_ENABLE_PAIR_STRINGMAKER)
+#include <utility>
+namespace Catch {
+    template<typename T1, typename T2>
+    struct StringMaker<std::pair<T1, T2> > {
+        static std::string convert(const std::pair<T1, T2>& pair) {
+            ReusableStringStream rss;
+            rss << "{ "
+                << ::Catch::Detail::stringify(pair.first)
+                << ", "
+                << ::Catch::Detail::stringify(pair.second)
+                << " }";
+            return rss.str();
+        }
+    };
 }
+#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>
+namespace Catch {
     namespace Detail {
-    template<typename InputIterator>
-    std::string rangeToString( InputIterator first, InputIterator last ) {
-        std::ostringstream oss;
-        oss << "{ ";
-        if( first != last ) {
-            oss << Catch::toString( *first );
-            for( ++first ; first != last ; ++first )
-                oss << ", " << Catch::toString( *first );
-        }
-        oss << " }";
-        return oss.str();
+        template<
+            typename Tuple,
+            std::size_t N = 0,
+            bool = (N < std::tuple_size<Tuple>::value)
+            >
+            struct TupleElementPrinter {
+            static void print(const Tuple& tuple, std::ostream& os) {
+                os << (N ? ", " : " ")
+                    << ::Catch::Detail::stringify(std::get<N>(tuple));
+                TupleElementPrinter<Tuple, N + 1>::print(tuple, os);
+            }
+        };
+
+        template<
+            typename Tuple,
+            std::size_t N
+        >
+            struct TupleElementPrinter<Tuple, N, false> {
+            static void print(const Tuple&, std::ostream&) {}
+        };
+
     }
-}
 
-} // end namespace Catch
+    template<typename ...Types>
+    struct StringMaker<std::tuple<Types...>> {
+        static std::string convert(const std::tuple<Types...>& tuple) {
+            ReusableStringStream rss;
+            rss << '{';
+            Detail::TupleElementPrinter<std::tuple<Types...>>::print(tuple, rss.get());
+            rss << " }";
+            return rss.str();
+        }
+    };
+}
+#endif // CATCH_CONFIG_ENABLE_TUPLE_STRINGMAKER
 
+#if defined(CATCH_CONFIG_ENABLE_VARIANT_STRINGMAKER) && defined(CATCH_CONFIG_CPP17_VARIANT)
+#include <variant>
 namespace Catch {
+    template<>
+    struct StringMaker<std::monostate> {
+        static std::string convert(const std::monostate&) {
+            return "{ }";
+        }
+    };
 
-template<typename LhsT, Internal::Operator Op, typename RhsT>
-class BinaryExpression;
+    template<typename... Elements>
+    struct StringMaker<std::variant<Elements...>> {
+        static std::string convert(const std::variant<Elements...>& variant) {
+            if (variant.valueless_by_exception()) {
+                return "{valueless variant}";
+            } else {
+                return std::visit(
+                    [](const auto& value) {
+                        return ::Catch::Detail::stringify(value);
+                    },
+                    variant
+                );
+            }
+        }
+    };
+}
+#endif // CATCH_CONFIG_ENABLE_VARIANT_STRINGMAKER
 
-template<typename ArgT, typename MatcherT>
-class MatchExpression;
+namespace Catch {
+    // Import begin/ end from std here
+    using std::begin;
+    using std::end;
+
+    namespace detail {
+        template <typename...>
+        struct void_type {
+            using type = void;
+        };
 
-// Wraps the LHS of an expression and overloads comparison operators
-// for also capturing those and RHS (if any)
-template<typename T>
-class ExpressionLhs : public DecomposedExpression {
-public:
-    ExpressionLhs( ResultBuilder& rb, T lhs ) : m_rb( rb ), m_lhs( lhs ), m_truthy(false) {}
+        template <typename T, typename = void>
+        struct is_range_impl : std::false_type {
+        };
 
-    ExpressionLhs& operator = ( const ExpressionLhs& );
+        template <typename T>
+        struct is_range_impl<T, typename void_type<decltype(begin(std::declval<T>()))>::type> : std::true_type {
+        };
+    } // namespace detail
 
-    template<typename RhsT>
-    BinaryExpression<T, Internal::IsEqualTo, RhsT const&>
-    operator == ( RhsT const& rhs ) {
-        return captureExpression<Internal::IsEqualTo>( rhs );
-    }
+    template <typename T>
+    struct is_range : detail::is_range_impl<T> {
+    };
 
-    template<typename RhsT>
-    BinaryExpression<T, Internal::IsNotEqualTo, RhsT const&>
-    operator != ( RhsT const& rhs ) {
-        return captureExpression<Internal::IsNotEqualTo>( rhs );
-    }
+#if defined(_MANAGED) // Managed types are never ranges
+    template <typename T>
+    struct is_range<T^> {
+        static const bool value = false;
+    };
+#endif
 
-    template<typename RhsT>
-    BinaryExpression<T, Internal::IsLessThan, RhsT const&>
-    operator < ( RhsT const& rhs ) {
-        return captureExpression<Internal::IsLessThan>( rhs );
+    template<typename Range>
+    std::string rangeToString( Range const& range ) {
+        return ::Catch::Detail::rangeToString( begin( range ), end( range ) );
     }
 
-    template<typename RhsT>
-    BinaryExpression<T, Internal::IsGreaterThan, RhsT const&>
-    operator > ( RhsT const& rhs ) {
-        return captureExpression<Internal::IsGreaterThan>( rhs );
+    // Handle vector<bool> specially
+    template<typename Allocator>
+    std::string rangeToString( std::vector<bool, Allocator> const& v ) {
+        ReusableStringStream rss;
+        rss << "{ ";
+        bool first = true;
+        for( bool b : v ) {
+            if( first )
+                first = false;
+            else
+                rss << ", ";
+            rss << ::Catch::Detail::stringify( b );
+        }
+        rss << " }";
+        return rss.str();
     }
 
-    template<typename RhsT>
-    BinaryExpression<T, Internal::IsLessThanOrEqualTo, RhsT const&>
-    operator <= ( RhsT const& rhs ) {
-        return captureExpression<Internal::IsLessThanOrEqualTo>( rhs );
-    }
+    template<typename R>
+    struct StringMaker<R, typename std::enable_if<is_range<R>::value && !::Catch::Detail::IsStreamInsertable<R>::value>::type> {
+        static std::string convert( R const& range ) {
+            return rangeToString( range );
+        }
+    };
 
-    template<typename RhsT>
-    BinaryExpression<T, Internal::IsGreaterThanOrEqualTo, RhsT const&>
-    operator >= ( RhsT const& rhs ) {
-        return captureExpression<Internal::IsGreaterThanOrEqualTo>( rhs );
-    }
+    template <typename T, int SZ>
+    struct StringMaker<T[SZ]> {
+        static std::string convert(T const(&arr)[SZ]) {
+            return rangeToString(arr);
+        }
+    };
 
-    BinaryExpression<T, Internal::IsEqualTo, bool> operator == ( bool rhs ) {
-        return captureExpression<Internal::IsEqualTo>( rhs );
-    }
+} // namespace Catch
 
-    BinaryExpression<T, Internal::IsNotEqualTo, bool> operator != ( bool rhs ) {
-        return captureExpression<Internal::IsNotEqualTo>( rhs );
-    }
+// Separate std::chrono::duration specialization
+#if defined(CATCH_CONFIG_ENABLE_CHRONO_STRINGMAKER)
+#include <ctime>
+#include <ratio>
+#include <chrono>
 
-    void endExpression() {
-        m_truthy = m_lhs ? true : false;
-        m_rb
-            .setResultType( m_truthy )
-            .endExpression( *this );
-    }
+namespace Catch {
 
-    virtual void reconstructExpression( std::string& dest ) const CATCH_OVERRIDE {
-        dest = Catch::toString( m_lhs );
-    }
+template <class Ratio>
+struct ratio_string {
+    static std::string symbol();
+};
 
-private:
-    template<Internal::Operator Op, typename RhsT>
-    BinaryExpression<T, Op, RhsT&> captureExpression( RhsT& rhs ) const {
-        return BinaryExpression<T, Op, RhsT&>( m_rb, m_lhs, rhs );
-    }
+template <class Ratio>
+std::string ratio_string<Ratio>::symbol() {
+    Catch::ReusableStringStream rss;
+    rss << '[' << Ratio::num << '/'
+        << Ratio::den << ']';
+    return rss.str();
+}
+template <>
+struct ratio_string<std::atto> {
+    static std::string symbol();
+};
+template <>
+struct ratio_string<std::femto> {
+    static std::string symbol();
+};
+template <>
+struct ratio_string<std::pico> {
+    static std::string symbol();
+};
+template <>
+struct ratio_string<std::nano> {
+    static std::string symbol();
+};
+template <>
+struct ratio_string<std::micro> {
+    static std::string symbol();
+};
+template <>
+struct ratio_string<std::milli> {
+    static std::string symbol();
+};
 
-    template<Internal::Operator Op>
-    BinaryExpression<T, Op, bool> captureExpression( bool rhs ) const {
-        return BinaryExpression<T, Op, bool>( m_rb, m_lhs, rhs );
-    }
+    ////////////
+    // std::chrono::duration specializations
+    template<typename Value, typename Ratio>
+    struct StringMaker<std::chrono::duration<Value, Ratio>> {
+        static std::string convert(std::chrono::duration<Value, Ratio> const& duration) {
+            ReusableStringStream rss;
+            rss << duration.count() << ' ' << ratio_string<Ratio>::symbol() << 's';
+            return rss.str();
+        }
+    };
+    template<typename Value>
+    struct StringMaker<std::chrono::duration<Value, std::ratio<1>>> {
+        static std::string convert(std::chrono::duration<Value, std::ratio<1>> const& duration) {
+            ReusableStringStream rss;
+            rss << duration.count() << " s";
+            return rss.str();
+        }
+    };
+    template<typename Value>
+    struct StringMaker<std::chrono::duration<Value, std::ratio<60>>> {
+        static std::string convert(std::chrono::duration<Value, std::ratio<60>> const& duration) {
+            ReusableStringStream rss;
+            rss << duration.count() << " m";
+            return rss.str();
+        }
+    };
+    template<typename Value>
+    struct StringMaker<std::chrono::duration<Value, std::ratio<3600>>> {
+        static std::string convert(std::chrono::duration<Value, std::ratio<3600>> const& duration) {
+            ReusableStringStream rss;
+            rss << duration.count() << " h";
+            return rss.str();
+        }
+    };
 
-private:
-    ResultBuilder& m_rb;
-    T m_lhs;
-    bool m_truthy;
-};
+    ////////////
+    // std::chrono::time_point specialization
+    // Generic time_point cannot be specialized, only std::chrono::time_point<system_clock>
+    template<typename Clock, typename Duration>
+    struct StringMaker<std::chrono::time_point<Clock, Duration>> {
+        static std::string convert(std::chrono::time_point<Clock, Duration> const& time_point) {
+            return ::Catch::Detail::stringify(time_point.time_since_epoch()) + " since epoch";
+        }
+    };
+    // std::chrono::time_point<system_clock> specialization
+    template<typename Duration>
+    struct StringMaker<std::chrono::time_point<std::chrono::system_clock, Duration>> {
+        static std::string convert(std::chrono::time_point<std::chrono::system_clock, Duration> const& time_point) {
+            auto converted = std::chrono::system_clock::to_time_t(time_point);
 
-template<typename LhsT, Internal::Operator Op, typename RhsT>
-class BinaryExpression : public DecomposedExpression {
-public:
-    BinaryExpression( ResultBuilder& rb, LhsT lhs, RhsT rhs )
-        : m_rb( rb ), m_lhs( lhs ), m_rhs( rhs ) {}
+#ifdef _MSC_VER
+            std::tm timeInfo = {};
+            gmtime_s(&timeInfo, &converted);
+#else
+            std::tm* timeInfo = std::gmtime(&converted);
+#endif
 
-    BinaryExpression& operator = ( BinaryExpression& );
+            auto const timeStampSize = sizeof("2017-01-16T17:06:45Z");
+            char timeStamp[timeStampSize];
+            const char * const fmt = "%Y-%m-%dT%H:%M:%SZ";
 
-    void endExpression() const {
-        m_rb
-            .setResultType( Internal::compare<Op>( m_lhs, m_rhs ) )
-            .endExpression( *this );
-    }
+#ifdef _MSC_VER
+            std::strftime(timeStamp, timeStampSize, fmt, &timeInfo);
+#else
+            std::strftime(timeStamp, timeStampSize, fmt, timeInfo);
+#endif
+            return std::string(timeStamp);
+        }
+    };
+}
+#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 ) )); \
+        } \
+    }; \
+}
 
-    virtual bool isBinaryExpression() const CATCH_OVERRIDE {
-        return true;
-    }
+#define CATCH_REGISTER_ENUM( enumName, ... ) INTERNAL_CATCH_REGISTER_ENUM( enumName, __VA_ARGS__ )
 
-    virtual void reconstructExpression( std::string& dest ) const CATCH_OVERRIDE {
-        std::string lhs = Catch::toString( m_lhs );
-        std::string rhs = Catch::toString( m_rhs );
-        char delim = lhs.size() + rhs.size() < 40 &&
-                     lhs.find('\n') == std::string::npos &&
-                     rhs.find('\n') == std::string::npos ? ' ' : '\n';
-        dest.reserve( 7 + lhs.size() + rhs.size() );
-                   // 2 for spaces around operator
-                   // 2 for operator
-                   // 2 for parentheses (conditionally added later)
-                   // 1 for negation (conditionally added later)
-        dest = lhs;
-        dest += delim;
-        dest += Internal::OperatorTraits<Op>::getName();
-        dest += delim;
-        dest += rhs;
-    }
+#ifdef _MSC_VER
+#pragma warning(pop)
+#endif
 
-private:
-    ResultBuilder& m_rb;
-    LhsT m_lhs;
-    RhsT m_rhs;
-};
+// end catch_tostring.h
+#include <iosfwd>
 
-template<typename ArgT, typename MatcherT>
-class MatchExpression : public DecomposedExpression {
-public:
-    MatchExpression( ArgT arg, MatcherT matcher, char const* matcherString )
-        : m_arg( arg ), m_matcher( matcher ), m_matcherString( matcherString ) {}
+#ifdef _MSC_VER
+#pragma warning(push)
+#pragma warning(disable:4389) // '==' : signed/unsigned mismatch
+#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
 
-    virtual bool isBinaryExpression() const CATCH_OVERRIDE {
-        return true;
-    }
+namespace Catch {
 
-    virtual void reconstructExpression( std::string& dest ) const CATCH_OVERRIDE {
-        std::string matcherAsString = m_matcher.toString();
-        dest = Catch::toString( m_arg );
-        dest += ' ';
-        if( matcherAsString == Detail::unprintableString )
-            dest += m_matcherString;
-        else
-            dest += matcherAsString;
-    }
+    struct ITransientExpression {
+        auto isBinaryExpression() const -> bool { return m_isBinaryExpression; }
+        auto getResult() const -> bool { return m_result; }
+        virtual void streamReconstructedExpression( std::ostream &os ) const = 0;
 
-private:
-    ArgT m_arg;
-    MatcherT m_matcher;
-    char const* m_matcherString;
-};
+        ITransientExpression( bool isBinaryExpression, bool result )
+        :   m_isBinaryExpression( isBinaryExpression ),
+            m_result( result )
+        {}
 
-} // end namespace Catch
+        // We don't actually need a virtual destructor, but many static analysers
+        // complain if it's not here :-(
+        virtual ~ITransientExpression();
 
+        bool m_isBinaryExpression;
+        bool m_result;
 
-namespace Catch {
+    };
 
-    template<typename T>
-    ExpressionLhs<T const&> ResultBuilder::operator <= ( T const& operand ) {
-        return ExpressionLhs<T const&>( *this, operand );
-    }
+    void formatReconstructedExpression( std::ostream &os, std::string const& lhs, StringRef op, std::string const& rhs );
 
-    inline ExpressionLhs<bool> ResultBuilder::operator <= ( bool value ) {
-        return ExpressionLhs<bool>( *this, value );
-    }
+    template<typename LhsT, typename RhsT>
+    class BinaryExpr  : public ITransientExpression {
+        LhsT m_lhs;
+        StringRef m_op;
+        RhsT m_rhs;
 
-    template<typename ArgT, typename MatcherT>
-    void ResultBuilder::captureMatch( ArgT const& arg, MatcherT const& matcher,
-                                             char const* matcherString ) {
-        MatchExpression<ArgT const&, MatcherT const&> expr( arg, matcher, matcherString );
-        setResultType( matcher.match( arg ) );
-        endExpression( expr );
-    }
+        void streamReconstructedExpression( std::ostream &os ) const override {
+            formatReconstructedExpression
+                    ( os, Catch::Detail::stringify( m_lhs ), m_op, Catch::Detail::stringify( m_rhs ) );
+        }
 
-} // namespace Catch
+    public:
+        BinaryExpr( bool comparisonResult, LhsT lhs, StringRef op, RhsT rhs )
+        :   ITransientExpression{ true, comparisonResult },
+            m_lhs( lhs ),
+            m_op( op ),
+            m_rhs( rhs )
+        {}
 
-// #included from: catch_message.h
-#define TWOBLUECUBES_CATCH_MESSAGE_H_INCLUDED
+        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");
+        }
 
-#include <string>
+        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");
+        }
 
-namespace Catch {
+        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");
+        }
 
-    struct MessageInfo {
-        MessageInfo(    std::string const& _macroName,
-                        SourceLineInfo const& _lineInfo,
-                        ResultWas::OfType _type );
+        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");
+        }
 
-        std::string macroName;
-        SourceLineInfo lineInfo;
-        ResultWas::OfType type;
-        std::string message;
-        unsigned int sequence;
+        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");
+        }
 
-        bool operator == ( MessageInfo const& other ) const {
-            return sequence == other.sequence;
+        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");
         }
-        bool operator < ( MessageInfo const& other ) const {
-            return sequence < other.sequence;
+
+        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");
         }
-    private:
-        static unsigned int globalCount;
     };
 
-    struct MessageBuilder {
-        MessageBuilder( std::string const& macroName,
-                        SourceLineInfo const& lineInfo,
-                        ResultWas::OfType type )
-        : m_info( macroName, lineInfo, type )
-        {}
+    template<typename LhsT>
+    class UnaryExpr : public ITransientExpression {
+        LhsT m_lhs;
 
-        template<typename T>
-        MessageBuilder& operator << ( T const& value ) {
-            m_stream << value;
-            return *this;
+        void streamReconstructedExpression( std::ostream &os ) const override {
+            os << Catch::Detail::stringify( m_lhs );
         }
 
-        MessageInfo m_info;
-        std::ostringstream m_stream;
+    public:
+        explicit UnaryExpr( LhsT lhs )
+        :   ITransientExpression{ false, static_cast<bool>(lhs) },
+            m_lhs( lhs )
+        {}
     };
 
-    class ScopedMessage {
+    // Specialised comparison functions to handle equality comparisons between ints and pointers (NULL deduces as an int)
+    template<typename LhsT, typename RhsT>
+    auto compareEqual( LhsT const& lhs, RhsT const& rhs ) -> bool { return static_cast<bool>(lhs == rhs); }
+    template<typename T>
+    auto compareEqual( T* const& lhs, int rhs ) -> bool { return lhs == reinterpret_cast<void const*>( rhs ); }
+    template<typename T>
+    auto compareEqual( T* const& lhs, long rhs ) -> bool { return lhs == reinterpret_cast<void const*>( rhs ); }
+    template<typename T>
+    auto compareEqual( int lhs, T* const& rhs ) -> bool { return reinterpret_cast<void const*>( lhs ) == rhs; }
+    template<typename T>
+    auto compareEqual( long lhs, T* const& rhs ) -> bool { return reinterpret_cast<void const*>( lhs ) == rhs; }
+
+    template<typename LhsT, typename RhsT>
+    auto compareNotEqual( LhsT const& lhs, RhsT&& rhs ) -> bool { return static_cast<bool>(lhs != rhs); }
+    template<typename T>
+    auto compareNotEqual( T* const& lhs, int rhs ) -> bool { return lhs != reinterpret_cast<void const*>( rhs ); }
+    template<typename T>
+    auto compareNotEqual( T* const& lhs, long rhs ) -> bool { return lhs != reinterpret_cast<void const*>( rhs ); }
+    template<typename T>
+    auto compareNotEqual( int lhs, T* const& rhs ) -> bool { return reinterpret_cast<void const*>( lhs ) != rhs; }
+    template<typename T>
+    auto compareNotEqual( long lhs, T* const& rhs ) -> bool { return reinterpret_cast<void const*>( lhs ) != rhs; }
+
+    template<typename LhsT>
+    class ExprLhs {
+        LhsT m_lhs;
     public:
-        ScopedMessage( MessageBuilder const& builder );
-        ScopedMessage( ScopedMessage const& other );
-        ~ScopedMessage();
+        explicit ExprLhs( LhsT lhs ) : m_lhs( lhs ) {}
 
-        MessageInfo m_info;
+        template<typename RhsT>
+        auto operator == ( RhsT const& rhs ) -> BinaryExpr<LhsT, RhsT const&> const {
+            return { compareEqual( m_lhs, rhs ), m_lhs, "==", rhs };
+        }
+        auto operator == ( bool rhs ) -> BinaryExpr<LhsT, bool> const {
+            return { m_lhs == rhs, m_lhs, "==", rhs };
+        }
+
+        template<typename RhsT>
+        auto operator != ( RhsT const& rhs ) -> BinaryExpr<LhsT, RhsT const&> const {
+            return { compareNotEqual( m_lhs, rhs ), m_lhs, "!=", rhs };
+        }
+        auto operator != ( bool rhs ) -> BinaryExpr<LhsT, bool> const {
+            return { 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& 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 };
+        }
+    };
+
+    void handleExpression( ITransientExpression const& expr );
+
+    template<typename T>
+    void handleExpression( ExprLhs<T> const& expr ) {
+        handleExpression( expr.makeUnaryExpr() );
+    }
+
+    struct Decomposer {
+        template<typename T>
+        auto operator <= ( T const& lhs ) -> ExprLhs<T const&> {
+            return ExprLhs<T const&>{ lhs };
+        }
+
+        auto operator <=( bool value ) -> ExprLhs<bool> {
+            return ExprLhs<bool>{ value };
+        }
     };
 
 } // end namespace Catch
 
-// #included from: catch_interfaces_capture.h
-#define TWOBLUECUBES_CATCH_INTERFACES_CAPTURE_H_INCLUDED
+#ifdef _MSC_VER
+#pragma warning(pop)
+#endif
+
+// end catch_decomposer.h
+// start catch_interfaces_capture.h
 
 #include <string>
+#include <chrono>
 
 namespace Catch {
 
-    class TestCase;
     class AssertionResult;
     struct AssertionInfo;
     struct SectionInfo;
     struct SectionEndInfo;
     struct MessageInfo;
-    class ScopedMessageBuilder;
+    struct MessageBuilder;
     struct Counts;
+    struct AssertionReaction;
+    struct SourceLineInfo;
 
-    struct IResultCapture {
+    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();
 
-        virtual void assertionEnded( AssertionResult const& result ) = 0;
         virtual bool sectionStarted(    SectionInfo const& sectionInfo,
                                         Counts& assertions ) = 0;
         virtual void sectionEnded( SectionEndInfo const& endInfo ) = 0;
         virtual void sectionEndedEarly( SectionEndInfo const& endInfo ) = 0;
-        virtual void pushScopedMessage( MessageInfo const& message ) = 0;
-        virtual void popScopedMessage( MessageInfo const& message ) = 0;
 
-        virtual std::string getCurrentTestName() const = 0;
-        virtual const AssertionResult* getLastResult() const = 0;
+        virtual auto acquireGeneratorTracker( StringRef generatorName, SourceLineInfo const& lineInfo ) -> IGeneratorTracker& = 0;
 
-        virtual void exceptionEarlyReported() = 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 benchmarkFailed( std::string const& error ) = 0;
+#endif // CATCH_CONFIG_ENABLE_BENCHMARKING
 
-        virtual void handleFatalErrorCondition( std::string const& message ) = 0;
+        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
+                (   AssertionInfo const& info,
+                    ITransientExpression const& expr,
+                    AssertionReaction& reaction ) = 0;
+        virtual void handleMessage
+                (   AssertionInfo const& info,
+                    ResultWas::OfType resultType,
+                    StringRef const& message,
+                    AssertionReaction& reaction ) = 0;
+        virtual void handleUnexpectedExceptionNotThrown
+                (   AssertionInfo const& info,
+                    AssertionReaction& reaction ) = 0;
+        virtual void handleUnexpectedInflightException
+                (   AssertionInfo const& info,
+                    std::string const& message,
+                    AssertionReaction& reaction ) = 0;
+        virtual void handleIncomplete
+                (   AssertionInfo const& info ) = 0;
+        virtual void handleNonExpr
+                (   AssertionInfo const &info,
+                    ResultWas::OfType resultType,
+                    AssertionReaction &reaction ) = 0;
 
         virtual bool lastAssertionPassed() = 0;
         virtual void assertionPassed() = 0;
-        virtual void assertionRun() = 0;
+
+        // Deprecated, do not use:
+        virtual std::string getCurrentTestName() const = 0;
+        virtual const AssertionResult* getLastResult() const = 0;
+        virtual void exceptionEarlyReported() = 0;
     };
 
     IResultCapture& getResultCapture();
 }
 
-// #included from: catch_debugger.h
-#define TWOBLUECUBES_CATCH_DEBUGGER_H_INCLUDED
+// end catch_interfaces_capture.h
+namespace Catch {
 
-// #included from: catch_platform.h
-#define TWOBLUECUBES_CATCH_PLATFORM_H_INCLUDED
+    struct TestFailureException{};
+    struct AssertionResultData;
+    struct IResultCapture;
+    class RunContext;
 
-#if defined(__MAC_OS_X_VERSION_MIN_REQUIRED)
-#  define CATCH_PLATFORM_MAC
-#elif  defined(__IPHONE_OS_VERSION_MIN_REQUIRED)
-#  define CATCH_PLATFORM_IPHONE
-#elif defined(linux) || defined(__linux) || defined(__linux__)
-#  define CATCH_PLATFORM_LINUX
-#elif defined(WIN32) || defined(__WIN32__) || defined(_WIN32) || defined(_MSC_VER)
-#  define CATCH_PLATFORM_WINDOWS
-#  if !defined(NOMINMAX) && !defined(CATCH_CONFIG_NO_NOMINMAX)
-#    define CATCH_DEFINES_NOMINMAX
-#  endif
-#  if !defined(WIN32_LEAN_AND_MEAN) && !defined(CATCH_CONFIG_NO_WIN32_LEAN_AND_MEAN)
-#    define CATCH_DEFINES_WIN32_LEAN_AND_MEAN
-#  endif
-#endif
+    class LazyExpression {
+        friend class AssertionHandler;
+        friend struct AssertionStats;
+        friend class RunContext;
 
-#include <string>
+        ITransientExpression const* m_transientExpression = nullptr;
+        bool m_isNegated;
+    public:
+        LazyExpression( bool isNegated );
+        LazyExpression( LazyExpression const& other );
+        LazyExpression& operator = ( LazyExpression const& ) = delete;
 
-namespace Catch{
+        explicit operator bool() const;
 
-    bool isDebuggerActive();
-    void writeToDebugConsole( std::string const& text );
-}
+        friend auto operator << ( std::ostream& os, LazyExpression const& lazyExpr ) -> std::ostream&;
+    };
 
-#ifdef CATCH_PLATFORM_MAC
+    struct AssertionReaction {
+        bool shouldDebugBreak = false;
+        bool shouldThrow = false;
+    };
 
-    // The following code snippet based on:
-    // http://cocoawithlove.com/2008/03/break-into-debugger.html
-    #if defined(__ppc64__) || defined(__ppc__)
-        #define CATCH_TRAP() \
-                __asm__("li r0, 20\nsc\nnop\nli r0, 37\nli r4, 2\nsc\nnop\n" \
-                : : : "memory","r0","r3","r4" ) /* NOLINT */
-    #else
-        #define CATCH_TRAP() __asm__("int $3\n" : : /* NOLINT */ )
-    #endif
+    class AssertionHandler {
+        AssertionInfo m_assertionInfo;
+        AssertionReaction m_reaction;
+        bool m_completed = false;
+        IResultCapture& m_resultCapture;
 
-#elif defined(CATCH_PLATFORM_LINUX)
-    // If we can use inline assembler, do it because this allows us to break
-    // directly at the location of the failing check instead of breaking inside
-    // raise() called from it, i.e. one stack frame below.
-    #if defined(__GNUC__) && (defined(__i386) || defined(__x86_64))
-        #define CATCH_TRAP() asm volatile ("int $3") /* NOLINT */
-    #else // Fall back to the generic way.
-        #include <signal.h>
+    public:
+        AssertionHandler
+            (   StringRef const& macroName,
+                SourceLineInfo const& lineInfo,
+                StringRef capturedExpression,
+                ResultDisposition::Flags resultDisposition );
+        ~AssertionHandler() {
+            if ( !m_completed ) {
+                m_resultCapture.handleIncomplete( m_assertionInfo );
+            }
+        }
 
-        #define CATCH_TRAP() raise(SIGTRAP)
-    #endif
-#elif defined(_MSC_VER)
-    #define CATCH_TRAP() __debugbreak()
-#elif defined(__MINGW32__)
-    extern "C" __declspec(dllimport) void __stdcall DebugBreak();
-    #define CATCH_TRAP() DebugBreak()
-#endif
+        template<typename T>
+        void handleExpr( ExprLhs<T> const& expr ) {
+            handleExpr( expr.makeUnaryExpr() );
+        }
+        void handleExpr( ITransientExpression const& expr );
 
-#ifdef CATCH_TRAP
-    #define CATCH_BREAK_INTO_DEBUGGER() if( Catch::isDebuggerActive() ) { CATCH_TRAP(); }
-#else
-    #define CATCH_BREAK_INTO_DEBUGGER() Catch::alwaysTrue();
-#endif
+        void handleMessage(ResultWas::OfType resultType, StringRef const& message);
+
+        void handleExceptionThrownAsExpected();
+        void handleUnexpectedExceptionNotThrown();
+        void handleExceptionNotThrownAsExpected();
+        void handleThrowingCallSkipped();
+        void handleUnexpectedInflightException();
+
+        void complete();
+        void setCompleted();
+
+        // query
+        auto allowThrows() const -> bool;
+    };
+
+    void handleExceptionMatchExpr( AssertionHandler& handler, std::string const& str, StringRef const& matcherString );
+
+} // namespace Catch
+
+// end catch_assertionhandler.h
+// start catch_message.h
 
-// #included from: catch_interfaces_runner.h
-#define TWOBLUECUBES_CATCH_INTERFACES_RUNNER_H_INCLUDED
+#include <string>
+#include <vector>
 
 namespace Catch {
-    class TestCase;
 
-    struct IRunner {
-        virtual ~IRunner();
-        virtual bool aborting() const = 0;
+    struct MessageInfo {
+        MessageInfo(    StringRef const& _macroName,
+                        SourceLineInfo const& _lineInfo,
+                        ResultWas::OfType _type );
+
+        StringRef macroName;
+        std::string message;
+        SourceLineInfo lineInfo;
+        ResultWas::OfType type;
+        unsigned int sequence;
+
+        bool operator == ( MessageInfo const& other ) const;
+        bool operator < ( MessageInfo const& other ) const;
+    private:
+        static unsigned int globalCount;
     };
-}
+
+    struct MessageStream {
+
+        template<typename T>
+        MessageStream& operator << ( T const& value ) {
+            m_stream << value;
+            return *this;
+        }
+
+        ReusableStringStream m_stream;
+    };
+
+    struct MessageBuilder : MessageStream {
+        MessageBuilder( StringRef const& macroName,
+                        SourceLineInfo const& lineInfo,
+                        ResultWas::OfType type );
+
+        template<typename T>
+        MessageBuilder& operator << ( T const& value ) {
+            m_stream << value;
+            return *this;
+        }
+
+        MessageInfo m_info;
+    };
+
+    class ScopedMessage {
+    public:
+        explicit ScopedMessage( MessageBuilder const& builder );
+        ScopedMessage( ScopedMessage& duplicate ) = delete;
+        ScopedMessage( ScopedMessage&& old );
+        ~ScopedMessage();
+
+        MessageInfo m_info;
+        bool m_moved;
+    };
+
+    class Capturer {
+        std::vector<MessageInfo> m_messages;
+        IResultCapture& m_resultCapture = getResultCapture();
+        size_t m_captured = 0;
+    public:
+        Capturer( StringRef macroName, SourceLineInfo const& lineInfo, ResultWas::OfType resultType, StringRef names );
+        ~Capturer();
+
+        void captureValue( size_t index, std::string const& value );
+
+        template<typename T>
+        void captureValues( size_t index, T const& value ) {
+            captureValue( index, Catch::Detail::stringify( value ) );
+        }
+
+        template<typename T, typename... Ts>
+        void captureValues( size_t index, T const& value, Ts const&... values ) {
+            captureValue( index, Catch::Detail::stringify(value) );
+            captureValues( index+1, values... );
+        }
+    };
+
+} // end namespace Catch
+
+// end catch_message.h
+#if !defined(CATCH_CONFIG_DISABLE)
 
 #if !defined(CATCH_CONFIG_DISABLE_STRINGIFICATION)
-# define CATCH_INTERNAL_STRINGIFY(expr) #expr
+  #define CATCH_INTERNAL_STRINGIFY(...) #__VA_ARGS__
 #else
-# define CATCH_INTERNAL_STRINGIFY(expr) "Disabled by CATCH_CONFIG_DISABLE_STRINGIFICATION"
+  #define CATCH_INTERNAL_STRINGIFY(...) "Disabled by CATCH_CONFIG_DISABLE_STRINGIFICATION"
 #endif
 
-#if defined(CATCH_CONFIG_FAST_COMPILE)
-///////////////////////////////////////////////////////////////////////////////
-// We can speedup compilation significantly by breaking into debugger lower in
-// the callstack, because then we don't have to expand CATCH_BREAK_INTO_DEBUGGER
-// macro in each assertion
-#define INTERNAL_CATCH_REACT( resultBuilder ) \
-    resultBuilder.react();
+#if defined(CATCH_CONFIG_FAST_COMPILE) || defined(CATCH_CONFIG_DISABLE_EXCEPTIONS)
 
 ///////////////////////////////////////////////////////////////////////////////
 // Another way to speed-up compilation is to omit local try-catch for REQUIRE*
 // macros.
-// This can potentially cause false negative, if the test code catches
-// the exception before it propagates back up to the runner.
-#define INTERNAL_CATCH_TEST_NO_TRY( macroName, resultDisposition, expr ) \
-    do { \
-        Catch::ResultBuilder __catchResult( macroName, CATCH_INTERNAL_LINEINFO, CATCH_INTERNAL_STRINGIFY(expr), resultDisposition ); \
-        __catchResult.setExceptionGuard(); \
-        CATCH_INTERNAL_SUPPRESS_PARENTHESES_WARNINGS \
-        ( __catchResult <= expr ).endExpression(); \
-        CATCH_INTERNAL_UNSUPPRESS_PARENTHESES_WARNINGS \
-        __catchResult.unsetExceptionGuard(); \
-        INTERNAL_CATCH_REACT( __catchResult ) \
-    } while( Catch::isTrue( false && static_cast<bool>( !!(expr) ) ) ) // expr 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 &&.
-
-#define INTERNAL_CHECK_THAT_NO_TRY( macroName, matcher, resultDisposition, arg ) \
-    do { \
-        Catch::ResultBuilder __catchResult( macroName, CATCH_INTERNAL_LINEINFO, CATCH_INTERNAL_STRINGIFY(arg) ", " CATCH_INTERNAL_STRINGIFY(matcher), resultDisposition ); \
-        __catchResult.setExceptionGuard(); \
-        __catchResult.captureMatch( arg, matcher, CATCH_INTERNAL_STRINGIFY(matcher) ); \
-        __catchResult.unsetExceptionGuard(); \
-        INTERNAL_CATCH_REACT( __catchResult ) \
-    } while( Catch::alwaysFalse() )
+#define INTERNAL_CATCH_TRY
+#define INTERNAL_CATCH_CATCH( capturer )
+
+#else // CATCH_CONFIG_FAST_COMPILE
+
+#define INTERNAL_CATCH_TRY try
+#define INTERNAL_CATCH_CATCH( handler ) catch(...) { handler.handleUnexpectedInflightException(); }
 
-#else
-///////////////////////////////////////////////////////////////////////////////
-// In the event of a failure works out if the debugger needs to be invoked
-// and/or an exception thrown and takes appropriate action.
-// This needs to be done as a macro so the debugger will stop in the user
-// source code rather than in Catch library code
-#define INTERNAL_CATCH_REACT( resultBuilder ) \
-    if( resultBuilder.shouldDebugBreak() ) CATCH_BREAK_INTO_DEBUGGER(); \
-    resultBuilder.react();
 #endif
 
+#define INTERNAL_CATCH_REACT( handler ) handler.complete();
+
 ///////////////////////////////////////////////////////////////////////////////
-#define INTERNAL_CATCH_TEST( macroName, resultDisposition, expr ) \
+#define INTERNAL_CATCH_TEST( macroName, resultDisposition, ... ) \
     do { \
-        Catch::ResultBuilder __catchResult( macroName, CATCH_INTERNAL_LINEINFO, CATCH_INTERNAL_STRINGIFY(expr), resultDisposition ); \
-        try { \
+        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 \
-            ( __catchResult <= expr ).endExpression(); \
-            CATCH_INTERNAL_UNSUPPRESS_PARENTHESES_WARNINGS \
-        } \
-        catch( ... ) { \
-            __catchResult.useActiveException( resultDisposition ); \
-        } \
-        INTERNAL_CATCH_REACT( __catchResult ) \
-    } while( Catch::isTrue( false && static_cast<bool>( !!(expr) ) ) ) // expr 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 &&.
+            catchAssertionHandler.handleExpr( Catch::Decomposer() <= __VA_ARGS__ ); \
+            CATCH_INTERNAL_STOP_WARNINGS_SUPPRESSION \
+        } INTERNAL_CATCH_CATCH( catchAssertionHandler ) \
+        INTERNAL_CATCH_REACT( catchAssertionHandler ) \
+    } while( (void)0, (false) && static_cast<bool>( !!(__VA_ARGS__) ) )
 
 ///////////////////////////////////////////////////////////////////////////////
-#define INTERNAL_CATCH_IF( macroName, resultDisposition, expr ) \
-    INTERNAL_CATCH_TEST( macroName, resultDisposition, expr ); \
+#define INTERNAL_CATCH_IF( macroName, resultDisposition, ... ) \
+    INTERNAL_CATCH_TEST( macroName, resultDisposition, __VA_ARGS__ ); \
     if( Catch::getResultCapture().lastAssertionPassed() )
 
 ///////////////////////////////////////////////////////////////////////////////
-#define INTERNAL_CATCH_ELSE( macroName, resultDisposition, expr ) \
-    INTERNAL_CATCH_TEST( macroName, resultDisposition, expr ); \
+#define INTERNAL_CATCH_ELSE( macroName, resultDisposition, ... ) \
+    INTERNAL_CATCH_TEST( macroName, resultDisposition, __VA_ARGS__ ); \
     if( !Catch::getResultCapture().lastAssertionPassed() )
 
 ///////////////////////////////////////////////////////////////////////////////
-#define INTERNAL_CATCH_NO_THROW( macroName, resultDisposition, expr ) \
+#define INTERNAL_CATCH_NO_THROW( macroName, resultDisposition, ... ) \
     do { \
-        Catch::ResultBuilder __catchResult( macroName, CATCH_INTERNAL_LINEINFO, CATCH_INTERNAL_STRINGIFY(expr), resultDisposition ); \
+        Catch::AssertionHandler catchAssertionHandler( macroName##_catch_sr, CATCH_INTERNAL_LINEINFO, CATCH_INTERNAL_STRINGIFY(__VA_ARGS__), resultDisposition ); \
         try { \
-            static_cast<void>(expr); \
-            __catchResult.captureResult( Catch::ResultWas::Ok ); \
+            static_cast<void>(__VA_ARGS__); \
+            catchAssertionHandler.handleExceptionNotThrownAsExpected(); \
         } \
         catch( ... ) { \
-            __catchResult.useActiveException( resultDisposition ); \
+            catchAssertionHandler.handleUnexpectedInflightException(); \
         } \
-        INTERNAL_CATCH_REACT( __catchResult ) \
-    } while( Catch::alwaysFalse() )
+        INTERNAL_CATCH_REACT( catchAssertionHandler ) \
+    } while( false )
 
 ///////////////////////////////////////////////////////////////////////////////
-#define INTERNAL_CATCH_THROWS( macroName, resultDisposition, matcher, expr ) \
+#define INTERNAL_CATCH_THROWS( macroName, resultDisposition, ... ) \
     do { \
-        Catch::ResultBuilder __catchResult( macroName, CATCH_INTERNAL_LINEINFO, CATCH_INTERNAL_STRINGIFY(expr), resultDisposition, CATCH_INTERNAL_STRINGIFY(matcher) ); \
-        if( __catchResult.allowThrows() ) \
+        Catch::AssertionHandler catchAssertionHandler( macroName##_catch_sr, CATCH_INTERNAL_LINEINFO, CATCH_INTERNAL_STRINGIFY(__VA_ARGS__), resultDisposition); \
+        if( catchAssertionHandler.allowThrows() ) \
             try { \
-                static_cast<void>(expr); \
-                __catchResult.captureResult( Catch::ResultWas::DidntThrowException ); \
+                static_cast<void>(__VA_ARGS__); \
+                catchAssertionHandler.handleUnexpectedExceptionNotThrown(); \
             } \
             catch( ... ) { \
-                __catchResult.captureExpectedException( matcher ); \
+                catchAssertionHandler.handleExceptionThrownAsExpected(); \
             } \
         else \
-            __catchResult.captureResult( Catch::ResultWas::Ok ); \
-        INTERNAL_CATCH_REACT( __catchResult ) \
-    } while( Catch::alwaysFalse() )
+            catchAssertionHandler.handleThrowingCallSkipped(); \
+        INTERNAL_CATCH_REACT( catchAssertionHandler ) \
+    } while( false )
 
 ///////////////////////////////////////////////////////////////////////////////
 #define INTERNAL_CATCH_THROWS_AS( macroName, exceptionType, resultDisposition, expr ) \
     do { \
-        Catch::ResultBuilder __catchResult( macroName, CATCH_INTERNAL_LINEINFO, CATCH_INTERNAL_STRINGIFY(expr) ", " CATCH_INTERNAL_STRINGIFY(exceptionType), resultDisposition ); \
-        if( __catchResult.allowThrows() ) \
+        Catch::AssertionHandler catchAssertionHandler( macroName##_catch_sr, CATCH_INTERNAL_LINEINFO, CATCH_INTERNAL_STRINGIFY(expr) ", " CATCH_INTERNAL_STRINGIFY(exceptionType), resultDisposition ); \
+        if( catchAssertionHandler.allowThrows() ) \
             try { \
                 static_cast<void>(expr); \
-                __catchResult.captureResult( Catch::ResultWas::DidntThrowException ); \
+                catchAssertionHandler.handleUnexpectedExceptionNotThrown(); \
             } \
-            catch( exceptionType ) { \
-                __catchResult.captureResult( Catch::ResultWas::Ok ); \
+            catch( exceptionType const& ) { \
+                catchAssertionHandler.handleExceptionThrownAsExpected(); \
             } \
             catch( ... ) { \
-                __catchResult.useActiveException( resultDisposition ); \
+                catchAssertionHandler.handleUnexpectedInflightException(); \
             } \
         else \
-            __catchResult.captureResult( Catch::ResultWas::Ok ); \
-        INTERNAL_CATCH_REACT( __catchResult ) \
-    } while( Catch::alwaysFalse() )
+            catchAssertionHandler.handleThrowingCallSkipped(); \
+        INTERNAL_CATCH_REACT( catchAssertionHandler ) \
+    } while( false )
 
 ///////////////////////////////////////////////////////////////////////////////
-#ifdef CATCH_CONFIG_VARIADIC_MACROS
-    #define INTERNAL_CATCH_MSG( macroName, messageType, resultDisposition, ... ) \
-        do { \
-            Catch::ResultBuilder __catchResult( macroName, CATCH_INTERNAL_LINEINFO, "", resultDisposition ); \
-            __catchResult << __VA_ARGS__ + ::Catch::StreamEndStop(); \
-            __catchResult.captureResult( messageType ); \
-            INTERNAL_CATCH_REACT( __catchResult ) \
-        } while( Catch::alwaysFalse() )
-#else
-    #define INTERNAL_CATCH_MSG( macroName, messageType, resultDisposition, log ) \
-        do { \
-            Catch::ResultBuilder __catchResult( macroName, CATCH_INTERNAL_LINEINFO, "", resultDisposition ); \
-            __catchResult << log + ::Catch::StreamEndStop(); \
-            __catchResult.captureResult( messageType ); \
-            INTERNAL_CATCH_REACT( __catchResult ) \
-        } while( Catch::alwaysFalse() )
-#endif
+#define INTERNAL_CATCH_MSG( macroName, messageType, resultDisposition, ... ) \
+    do { \
+        Catch::AssertionHandler catchAssertionHandler( macroName##_catch_sr, CATCH_INTERNAL_LINEINFO, Catch::StringRef(), resultDisposition ); \
+        catchAssertionHandler.handleMessage( messageType, ( Catch::MessageStream() << __VA_ARGS__ + ::Catch::StreamEndStop() ).m_stream.str() ); \
+        INTERNAL_CATCH_REACT( catchAssertionHandler ) \
+    } while( false )
+
+///////////////////////////////////////////////////////////////////////////////
+#define INTERNAL_CATCH_CAPTURE( varName, macroName, ... ) \
+    auto varName = Catch::Capturer( macroName, CATCH_INTERNAL_LINEINFO, Catch::ResultWas::Info, #__VA_ARGS__ ); \
+    varName.captureValues( 0, __VA_ARGS__ )
 
 ///////////////////////////////////////////////////////////////////////////////
 #define INTERNAL_CATCH_INFO( macroName, log ) \
-    Catch::ScopedMessage INTERNAL_CATCH_UNIQUE_NAME( scopedMessage ) = Catch::MessageBuilder( macroName, CATCH_INTERNAL_LINEINFO, Catch::ResultWas::Info ) << log;
+    Catch::ScopedMessage INTERNAL_CATCH_UNIQUE_NAME( scopedMessage )( Catch::MessageBuilder( macroName##_catch_sr, CATCH_INTERNAL_LINEINFO, Catch::ResultWas::Info ) << log );
 
 ///////////////////////////////////////////////////////////////////////////////
-#define INTERNAL_CHECK_THAT( macroName, matcher, resultDisposition, arg ) \
+#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 { \
-        Catch::ResultBuilder __catchResult( macroName, CATCH_INTERNAL_LINEINFO, CATCH_INTERNAL_STRINGIFY(arg) ", " CATCH_INTERNAL_STRINGIFY(matcher), resultDisposition ); \
-        try { \
-            __catchResult.captureMatch( arg, matcher, CATCH_INTERNAL_STRINGIFY(matcher) ); \
-        } catch( ... ) { \
-            __catchResult.useActiveException( resultDisposition | Catch::ResultDisposition::ContinueOnFailure ); \
-        } \
-        INTERNAL_CATCH_REACT( __catchResult ) \
-    } while( Catch::alwaysFalse() )
+        Catch::AssertionHandler catchAssertionHandler( macroName##_catch_sr, CATCH_INTERNAL_LINEINFO, CATCH_INTERNAL_STRINGIFY(__VA_ARGS__) ", " CATCH_INTERNAL_STRINGIFY(matcher), resultDisposition ); \
+        if( catchAssertionHandler.allowThrows() ) \
+            try { \
+                static_cast<void>(__VA_ARGS__); \
+                catchAssertionHandler.handleUnexpectedExceptionNotThrown(); \
+            } \
+            catch( ... ) { \
+                Catch::handleExceptionMatchExpr( catchAssertionHandler, matcher, #matcher##_catch_sr ); \
+            } \
+        else \
+            catchAssertionHandler.handleThrowingCallSkipped(); \
+        INTERNAL_CATCH_REACT( catchAssertionHandler ) \
+    } while( false )
 
-// #included from: internal/catch_section.h
-#define TWOBLUECUBES_CATCH_SECTION_H_INCLUDED
+#endif // CATCH_CONFIG_DISABLE
 
-// #included from: catch_section_info.h
-#define TWOBLUECUBES_CATCH_SECTION_INFO_H_INCLUDED
+// end catch_capture.hpp
+// start catch_section.h
 
-// #included from: catch_totals.hpp
-#define TWOBLUECUBES_CATCH_TOTALS_HPP_INCLUDED
+// start catch_section_info.h
+
+// start catch_totals.h
 
 #include <cstddef>
 
 namespace Catch {
 
     struct Counts {
-        Counts() : passed( 0 ), failed( 0 ), failedButOk( 0 ) {}
-
-        Counts operator - ( Counts const& other ) const {
-            Counts diff;
-            diff.passed = passed - other.passed;
-            diff.failed = failed - other.failed;
-            diff.failedButOk = failedButOk - other.failedButOk;
-            return diff;
-        }
-        Counts& operator += ( Counts const& other ) {
-            passed += other.passed;
-            failed += other.failed;
-            failedButOk += other.failedButOk;
-            return *this;
-        }
+        Counts operator - ( Counts const& other ) const;
+        Counts& operator += ( Counts const& other );
 
-        std::size_t total() const {
-            return passed + failed + failedButOk;
-        }
-        bool allPassed() const {
-            return failed == 0 && failedButOk == 0;
-        }
-        bool allOk() const {
-            return failed == 0;
-        }
+        std::size_t total() const;
+        bool allPassed() const;
+        bool allOk() const;
 
-        std::size_t passed;
-        std::size_t failed;
-        std::size_t failedButOk;
+        std::size_t passed = 0;
+        std::size_t failed = 0;
+        std::size_t failedButOk = 0;
     };
 
     struct Totals {
 
-        Totals operator - ( Totals const& other ) const {
-            Totals diff;
-            diff.assertions = assertions - other.assertions;
-            diff.testCases = testCases - other.testCases;
-            return diff;
-        }
-
-        Totals delta( Totals const& prevTotals ) const {
-            Totals diff = *this - prevTotals;
-            if( diff.assertions.failed > 0 )
-                ++diff.testCases.failed;
-            else if( diff.assertions.failedButOk > 0 )
-                ++diff.testCases.failedButOk;
-            else
-                ++diff.testCases.passed;
-            return diff;
-        }
+        Totals operator - ( Totals const& other ) const;
+        Totals& operator += ( Totals const& other );
 
-        Totals& operator += ( Totals const& other ) {
-            assertions += other.assertions;
-            testCases += other.testCases;
-            return *this;
-        }
+        Totals delta( Totals const& prevTotals ) const;
 
+        int error = 0;
         Counts assertions;
         Counts testCases;
     };
 }
 
+// end catch_totals.h
 #include <string>
 
 namespace Catch {
 
     struct SectionInfo {
+        SectionInfo
+            (   SourceLineInfo const& _lineInfo,
+                std::string const& _name );
+
+        // Deprecated
         SectionInfo
             (   SourceLineInfo const& _lineInfo,
                 std::string const& _name,
-                std::string const& _description = std::string() );
+                std::string const& ) : SectionInfo( _lineInfo, _name ) {}
 
         std::string name;
-        std::string description;
+        std::string description; // !Deprecated: this will always be empty
         SourceLineInfo lineInfo;
     };
 
     struct SectionEndInfo {
-        SectionEndInfo( SectionInfo const& _sectionInfo, Counts const& _prevAssertions, double _durationInSeconds )
-        : sectionInfo( _sectionInfo ), prevAssertions( _prevAssertions ), durationInSeconds( _durationInSeconds )
-        {}
-
         SectionInfo sectionInfo;
         Counts prevAssertions;
         double durationInSeconds;
@@ -2435,36 +2881,29 @@ namespace Catch {
 
 } // end namespace Catch
 
-// #included from: catch_timer.h
-#define TWOBLUECUBES_CATCH_TIMER_H_INCLUDED
+// end catch_section_info.h
+// start catch_timer.h
 
-#ifdef _MSC_VER
+#include <cstdint>
 
 namespace Catch {
-    typedef unsigned long long UInt64;
-}
-#else
-#include <stdint.h>
-namespace Catch {
-    typedef uint64_t UInt64;
-}
-#endif
 
-namespace Catch {
+    auto getCurrentNanosecondsSinceEpoch() -> uint64_t;
+    auto getEstimatedClockResolution() -> uint64_t;
+
     class Timer {
+        uint64_t m_nanoseconds = 0;
     public:
-        Timer() : m_ticks( 0 ) {}
         void start();
-        unsigned int getElapsedMicroseconds() const;
-        unsigned int getElapsedMilliseconds() const;
-        double getElapsedSeconds() const;
-
-    private:
-        UInt64 m_ticks;
+        auto getElapsedNanoseconds() const -> uint64_t;
+        auto getElapsedMicroseconds() const -> uint64_t;
+        auto getElapsedMilliseconds() const -> unsigned int;
+        auto getElapsedSeconds() const -> double;
     };
 
 } // namespace Catch
 
+// end catch_timer.h
 #include <string>
 
 namespace Catch {
@@ -2475,7 +2914,7 @@ namespace Catch {
         ~Section();
 
         // This indicates whether the section should be executed or not
-        operator bool() const;
+        explicit operator bool() const;
 
     private:
         SectionInfo m_info;
@@ -2488,277 +2927,120 @@ namespace Catch {
 
 } // end namespace Catch
 
-#ifdef CATCH_CONFIG_VARIADIC_MACROS
-    #define INTERNAL_CATCH_SECTION( ... ) \
-        if( Catch::Section const& INTERNAL_CATCH_UNIQUE_NAME( catch_internal_Section ) = Catch::SectionInfo( CATCH_INTERNAL_LINEINFO, __VA_ARGS__ ) )
-#else
-    #define INTERNAL_CATCH_SECTION( name, desc ) \
-        if( Catch::Section const& INTERNAL_CATCH_UNIQUE_NAME( catch_internal_Section ) = Catch::SectionInfo( CATCH_INTERNAL_LINEINFO, name, desc ) )
-#endif
+#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_STOP_WARNINGS_SUPPRESSION
 
-// #included from: internal/catch_generators.hpp
-#define TWOBLUECUBES_CATCH_GENERATORS_HPP_INCLUDED
+#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_STOP_WARNINGS_SUPPRESSION
+
+// end catch_section.h
+// start catch_interfaces_exception.h
+
+// start catch_interfaces_registry_hub.h
 
-#include <vector>
 #include <string>
-#include <stdlib.h>
+#include <memory>
 
 namespace Catch {
 
-template<typename T>
-struct IGenerator {
-    virtual ~IGenerator() {}
-    virtual T getValue( std::size_t index ) const = 0;
-    virtual std::size_t size () const = 0;
-};
-
-template<typename T>
-class BetweenGenerator : public IGenerator<T> {
-public:
-    BetweenGenerator( T from, T to ) : m_from( from ), m_to( to ){}
+    class TestCase;
+    struct ITestCaseRegistry;
+    struct IExceptionTranslatorRegistry;
+    struct IExceptionTranslator;
+    struct IReporterRegistry;
+    struct IReporterFactory;
+    struct ITagAliasRegistry;
+    struct IMutableEnumValuesRegistry;
 
-    virtual T getValue( std::size_t index ) const {
-        return m_from+static_cast<int>( index );
-    }
+    class StartupExceptionRegistry;
 
-    virtual std::size_t size() const {
-        return static_cast<std::size_t>( 1+m_to-m_from );
-    }
+    using IReporterFactoryPtr = std::shared_ptr<IReporterFactory>;
 
-private:
+    struct IRegistryHub {
+        virtual ~IRegistryHub();
 
-    T m_from;
-    T m_to;
-};
+        virtual IReporterRegistry const& getReporterRegistry() const = 0;
+        virtual ITestCaseRegistry const& getTestCaseRegistry() const = 0;
+        virtual ITagAliasRegistry const& getTagAliasRegistry() const = 0;
+        virtual IExceptionTranslatorRegistry const& getExceptionTranslatorRegistry() const = 0;
 
-template<typename T>
-class ValuesGenerator : public IGenerator<T> {
-public:
-    ValuesGenerator(){}
+        virtual StartupExceptionRegistry const& getStartupExceptionRegistry() const = 0;
+    };
 
-    void add( T value ) {
-        m_values.push_back( value );
-    }
+    struct IMutableRegistryHub {
+        virtual ~IMutableRegistryHub();
+        virtual void registerReporter( std::string const& name, IReporterFactoryPtr const& factory ) = 0;
+        virtual void registerListener( IReporterFactoryPtr const& factory ) = 0;
+        virtual void registerTest( TestCase const& testInfo ) = 0;
+        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;
+    };
 
-    virtual T getValue( std::size_t index ) const {
-        return m_values[index];
-    }
+    IRegistryHub const& getRegistryHub();
+    IMutableRegistryHub& getMutableRegistryHub();
+    void cleanUp();
+    std::string translateActiveException();
 
-    virtual std::size_t size() const {
-        return m_values.size();
-    }
+}
 
-private:
-    std::vector<T> m_values;
-};
+// end catch_interfaces_registry_hub.h
+#if defined(CATCH_CONFIG_DISABLE)
+    #define INTERNAL_CATCH_TRANSLATE_EXCEPTION_NO_REG( translatorName, signature) \
+        static std::string translatorName( signature )
+#endif
 
-template<typename T>
-class CompositeGenerator {
-public:
-    CompositeGenerator() : m_totalSize( 0 ) {}
+#include <exception>
+#include <string>
+#include <vector>
 
-    // *** Move semantics, similar to auto_ptr ***
-    CompositeGenerator( CompositeGenerator& other )
-    :   m_fileInfo( other.m_fileInfo ),
-        m_totalSize( 0 )
-    {
-        move( other );
-    }
+namespace Catch {
+    using exceptionTranslateFunction = std::string(*)();
 
-    CompositeGenerator& setFileInfo( const char* fileInfo ) {
-        m_fileInfo = fileInfo;
-        return *this;
-    }
+    struct IExceptionTranslator;
+    using ExceptionTranslators = std::vector<std::unique_ptr<IExceptionTranslator const>>;
 
-    ~CompositeGenerator() {
-        deleteAll( m_composed );
-    }
+    struct IExceptionTranslator {
+        virtual ~IExceptionTranslator();
+        virtual std::string translate( ExceptionTranslators::const_iterator it, ExceptionTranslators::const_iterator itEnd ) const = 0;
+    };
 
-    operator T () const {
-        size_t overallIndex = getCurrentContext().getGeneratorIndex( m_fileInfo, m_totalSize );
+    struct IExceptionTranslatorRegistry {
+        virtual ~IExceptionTranslatorRegistry();
 
-        typename std::vector<const IGenerator<T>*>::const_iterator it = m_composed.begin();
-        typename std::vector<const IGenerator<T>*>::const_iterator itEnd = m_composed.end();
-        for( size_t index = 0; it != itEnd; ++it )
-        {
-            const IGenerator<T>* generator = *it;
-            if( overallIndex >= index && overallIndex < index + generator->size() )
-            {
-                return generator->getValue( overallIndex-index );
-            }
-            index += generator->size();
-        }
-        CATCH_INTERNAL_ERROR( "Indexed past end of generated range" );
-        return T(); // Suppress spurious "not all control paths return a value" warning in Visual Studio - if you know how to fix this please do so
-    }
+        virtual std::string translateActiveException() const = 0;
+    };
 
-    void add( const IGenerator<T>* generator ) {
-        m_totalSize += generator->size();
-        m_composed.push_back( generator );
-    }
-
-    CompositeGenerator& then( CompositeGenerator& other ) {
-        move( other );
-        return *this;
-    }
-
-    CompositeGenerator& then( T value ) {
-        ValuesGenerator<T>* valuesGen = new ValuesGenerator<T>();
-        valuesGen->add( value );
-        add( valuesGen );
-        return *this;
-    }
-
-private:
-
-    void move( CompositeGenerator& other ) {
-        m_composed.insert( m_composed.end(), other.m_composed.begin(), other.m_composed.end() );
-        m_totalSize += other.m_totalSize;
-        other.m_composed.clear();
-    }
-
-    std::vector<const IGenerator<T>*> m_composed;
-    std::string m_fileInfo;
-    size_t m_totalSize;
-};
-
-namespace Generators
-{
-    template<typename T>
-    CompositeGenerator<T> between( T from, T to ) {
-        CompositeGenerator<T> generators;
-        generators.add( new BetweenGenerator<T>( from, to ) );
-        return generators;
-    }
-
-    template<typename T>
-    CompositeGenerator<T> values( T val1, T val2 ) {
-        CompositeGenerator<T> generators;
-        ValuesGenerator<T>* valuesGen = new ValuesGenerator<T>();
-        valuesGen->add( val1 );
-        valuesGen->add( val2 );
-        generators.add( valuesGen );
-        return generators;
-    }
-
-    template<typename T>
-    CompositeGenerator<T> values( T val1, T val2, T val3 ){
-        CompositeGenerator<T> generators;
-        ValuesGenerator<T>* valuesGen = new ValuesGenerator<T>();
-        valuesGen->add( val1 );
-        valuesGen->add( val2 );
-        valuesGen->add( val3 );
-        generators.add( valuesGen );
-        return generators;
-    }
-
-    template<typename T>
-    CompositeGenerator<T> values( T val1, T val2, T val3, T val4 ) {
-        CompositeGenerator<T> generators;
-        ValuesGenerator<T>* valuesGen = new ValuesGenerator<T>();
-        valuesGen->add( val1 );
-        valuesGen->add( val2 );
-        valuesGen->add( val3 );
-        valuesGen->add( val4 );
-        generators.add( valuesGen );
-        return generators;
-    }
-
-} // end namespace Generators
-
-using namespace Generators;
-
-} // end namespace Catch
-
-#define INTERNAL_CATCH_LINESTR2( line ) #line
-#define INTERNAL_CATCH_LINESTR( line ) INTERNAL_CATCH_LINESTR2( line )
-
-#define INTERNAL_CATCH_GENERATE( expr ) expr.setFileInfo( __FILE__ "(" INTERNAL_CATCH_LINESTR( __LINE__ ) ")" )
-
-// #included from: internal/catch_interfaces_exception.h
-#define TWOBLUECUBES_CATCH_INTERFACES_EXCEPTION_H_INCLUDED
-
-#include <string>
-#include <vector>
-
-// #included from: catch_interfaces_registry_hub.h
-#define TWOBLUECUBES_CATCH_INTERFACES_REGISTRY_HUB_H_INCLUDED
-
-#include <string>
-
-namespace Catch {
-
-    class TestCase;
-    struct ITestCaseRegistry;
-    struct IExceptionTranslatorRegistry;
-    struct IExceptionTranslator;
-    struct IReporterRegistry;
-    struct IReporterFactory;
-    struct ITagAliasRegistry;
-
-    struct IRegistryHub {
-        virtual ~IRegistryHub();
-
-        virtual IReporterRegistry const& getReporterRegistry() const = 0;
-        virtual ITestCaseRegistry const& getTestCaseRegistry() const = 0;
-        virtual ITagAliasRegistry const& getTagAliasRegistry() const = 0;
-
-        virtual IExceptionTranslatorRegistry& getExceptionTranslatorRegistry() = 0;
-    };
-
-    struct IMutableRegistryHub {
-        virtual ~IMutableRegistryHub();
-        virtual void registerReporter( std::string const& name, Ptr<IReporterFactory> const& factory ) = 0;
-        virtual void registerListener( Ptr<IReporterFactory> const& factory ) = 0;
-        virtual void registerTest( TestCase const& testInfo ) = 0;
-        virtual void registerTranslator( const IExceptionTranslator* translator ) = 0;
-        virtual void registerTagAlias( std::string const& alias, std::string const& tag, SourceLineInfo const& lineInfo ) = 0;
-    };
-
-    IRegistryHub& getRegistryHub();
-    IMutableRegistryHub& getMutableRegistryHub();
-    void cleanUp();
-    std::string translateActiveException();
-
-}
-
-namespace Catch {
-
-    typedef std::string(*exceptionTranslateFunction)();
-
-    struct IExceptionTranslator;
-    typedef std::vector<const IExceptionTranslator*> ExceptionTranslators;
-
-    struct IExceptionTranslator {
-        virtual ~IExceptionTranslator();
-        virtual std::string translate( ExceptionTranslators::const_iterator it, ExceptionTranslators::const_iterator itEnd ) const = 0;
-    };
-
-    struct IExceptionTranslatorRegistry {
-        virtual ~IExceptionTranslatorRegistry();
-
-        virtual std::string translateActiveException() const = 0;
-    };
-
-    class ExceptionTranslatorRegistrar {
-        template<typename T>
-        class ExceptionTranslator : public IExceptionTranslator {
-        public:
+    class ExceptionTranslatorRegistrar {
+        template<typename T>
+        class ExceptionTranslator : public IExceptionTranslator {
+        public:
 
             ExceptionTranslator( std::string(*translateFunction)( T& ) )
             : m_translateFunction( translateFunction )
             {}
 
-            virtual std::string translate( ExceptionTranslators::const_iterator it, ExceptionTranslators::const_iterator itEnd ) const CATCH_OVERRIDE {
+            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 )
-                        throw;
+                        std::rethrow_exception(std::current_exception());
                     else
                         return (*it)->translate( it+1, itEnd );
                 }
                 catch( T& ex ) {
                     return m_translateFunction( ex );
                 }
+#endif
             }
 
         protected:
@@ -2777,62 +3059,56 @@ namespace Catch {
 ///////////////////////////////////////////////////////////////////////////////
 #define INTERNAL_CATCH_TRANSLATE_EXCEPTION2( translatorName, signature ) \
     static std::string translatorName( signature ); \
-    namespace{ Catch::ExceptionTranslatorRegistrar INTERNAL_CATCH_UNIQUE_NAME( catch_internal_ExceptionRegistrar )( &translatorName ); }\
+    CATCH_INTERNAL_START_WARNINGS_SUPPRESSION \
+    CATCH_INTERNAL_SUPPRESS_GLOBALS_WARNINGS \
+    namespace{ Catch::ExceptionTranslatorRegistrar INTERNAL_CATCH_UNIQUE_NAME( catch_internal_ExceptionRegistrar )( &translatorName ); } \
+    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 )
 
-// #included from: internal/catch_approx.hpp
-#define TWOBLUECUBES_CATCH_APPROX_HPP_INCLUDED
+// end catch_interfaces_exception.h
+// start catch_approx.h
 
-#include <cmath>
-#include <limits>
-
-#if defined(CATCH_CONFIG_CPP11_TYPE_TRAITS)
 #include <type_traits>
-#endif
 
 namespace Catch {
 namespace Detail {
 
     class Approx {
+    private:
+        bool equalityComparisonImpl(double other) const;
+        // Validates the new margin (margin >= 0)
+        // out-of-line to avoid including stdexcept in the header
+        void setMargin(double margin);
+        // Validates the new epsilon (0 < epsilon < 1)
+        // out-of-line to avoid including stdexcept in the header
+        void setEpsilon(double epsilon);
+
     public:
-        explicit Approx ( double value )
-        :   m_epsilon( std::numeric_limits<float>::epsilon()*100 ),
-            m_margin( 0.0 ),
-            m_scale( 1.0 ),
-            m_value( value )
-        {}
+        explicit Approx ( double value );
 
-        static Approx custom() {
-            return Approx( 0 );
-        }
+        static Approx custom();
 
-#if defined(CATCH_CONFIG_CPP11_TYPE_TRAITS)
+        Approx operator-() const;
 
         template <typename T, typename = typename std::enable_if<std::is_constructible<double, T>::value>::type>
-        Approx operator()( T value ) {
+        Approx operator()( T const& value ) const {
             Approx approx( static_cast<double>(value) );
-            approx.epsilon( m_epsilon );
-            approx.margin( m_margin );
-            approx.scale( m_scale );
+            approx.m_epsilon = m_epsilon;
+            approx.m_margin = m_margin;
+            approx.m_scale = m_scale;
             return approx;
         }
 
         template <typename T, typename = typename std::enable_if<std::is_constructible<double, T>::value>::type>
-        explicit Approx( T value ): Approx(static_cast<double>(value))
+        explicit Approx( T const& value ): Approx(static_cast<double>(value))
         {}
 
         template <typename T, typename = typename std::enable_if<std::is_constructible<double, T>::value>::type>
         friend bool operator == ( const T& lhs, Approx const& rhs ) {
-            // Thanks to Richard Harris for his help refining this formula
-            auto lhs_v = double(lhs);
-            bool relativeOK = std::fabs(lhs_v - rhs.m_value) < rhs.m_epsilon * (rhs.m_scale + (std::max)(std::fabs(lhs_v), std::fabs(rhs.m_value)));
-            if (relativeOK) {
-                return true;
-            }
-
-            return std::fabs(lhs_v - rhs.m_value) <= rhs.m_margin;
+            auto lhs_v = static_cast<double>(lhs);
+            return rhs.equalityComparisonImpl(lhs_v);
         }
 
         template <typename T, typename = typename std::enable_if<std::is_constructible<double, T>::value>::type>
@@ -2841,121 +3117,56 @@ namespace Detail {
         }
 
         template <typename T, typename = typename std::enable_if<std::is_constructible<double, T>::value>::type>
-        friend bool operator != ( T lhs, Approx const& rhs ) {
+        friend bool operator != ( T const& lhs, Approx const& rhs ) {
             return !operator==( lhs, rhs );
         }
 
         template <typename T, typename = typename std::enable_if<std::is_constructible<double, T>::value>::type>
-        friend bool operator != ( Approx const& lhs, T rhs ) {
+        friend bool operator != ( Approx const& lhs, T const& rhs ) {
             return !operator==( rhs, lhs );
         }
 
         template <typename T, typename = typename std::enable_if<std::is_constructible<double, T>::value>::type>
-        friend bool operator <= ( T lhs, Approx const& rhs ) {
-            return double(lhs) < rhs.m_value || lhs == rhs;
+        friend bool operator <= ( T const& lhs, Approx const& rhs ) {
+            return static_cast<double>(lhs) < rhs.m_value || lhs == rhs;
         }
 
         template <typename T, typename = typename std::enable_if<std::is_constructible<double, T>::value>::type>
-        friend bool operator <= ( Approx const& lhs, T rhs ) {
-            return lhs.m_value < double(rhs) || lhs == rhs;
+        friend bool operator <= ( Approx const& lhs, T const& rhs ) {
+            return lhs.m_value < static_cast<double>(rhs) || lhs == rhs;
         }
 
         template <typename T, typename = typename std::enable_if<std::is_constructible<double, T>::value>::type>
-        friend bool operator >= ( T lhs, Approx const& rhs ) {
-            return double(lhs) > rhs.m_value || lhs == rhs;
+        friend bool operator >= ( T const& lhs, Approx const& rhs ) {
+            return static_cast<double>(lhs) > rhs.m_value || lhs == rhs;
         }
 
         template <typename T, typename = typename std::enable_if<std::is_constructible<double, T>::value>::type>
-        friend bool operator >= ( Approx const& lhs, T rhs ) {
-            return lhs.m_value > double(rhs) || lhs == rhs;
+        friend bool operator >= ( Approx const& lhs, T const& rhs ) {
+            return lhs.m_value > static_cast<double>(rhs) || lhs == rhs;
         }
 
         template <typename T, typename = typename std::enable_if<std::is_constructible<double, T>::value>::type>
-        Approx& epsilon( T newEpsilon ) {
-            m_epsilon = double(newEpsilon);
+        Approx& epsilon( T const& newEpsilon ) {
+            double epsilonAsDouble = static_cast<double>(newEpsilon);
+            setEpsilon(epsilonAsDouble);
             return *this;
         }
 
         template <typename T, typename = typename std::enable_if<std::is_constructible<double, T>::value>::type>
-        Approx& margin( T newMargin ) {
-            m_margin = double(newMargin);
+        Approx& margin( T const& newMargin ) {
+            double marginAsDouble = static_cast<double>(newMargin);
+            setMargin(marginAsDouble);
             return *this;
         }
 
         template <typename T, typename = typename std::enable_if<std::is_constructible<double, T>::value>::type>
-        Approx& scale( T newScale ) {
-            m_scale = double(newScale);
-            return *this;
-        }
-
-#else
-
-        Approx operator()( double value ) {
-            Approx approx( value );
-            approx.epsilon( m_epsilon );
-            approx.margin( m_margin );
-            approx.scale( m_scale );
-            return approx;
-        }
-
-        friend bool operator == ( double lhs, Approx const& rhs ) {
-            // Thanks to Richard Harris for his help refining this formula
-            bool relativeOK = std::fabs( lhs - rhs.m_value ) < rhs.m_epsilon * (rhs.m_scale + (std::max)( std::fabs(lhs), std::fabs(rhs.m_value) ) );
-            if (relativeOK) {
-                return true;
-            }
-            return std::fabs(lhs - rhs.m_value) <= rhs.m_margin;
-        }
-
-        friend bool operator == ( Approx const& lhs, double rhs ) {
-            return operator==( rhs, lhs );
-        }
-
-        friend bool operator != ( double lhs, Approx const& rhs ) {
-            return !operator==( lhs, rhs );
-        }
-
-        friend bool operator != ( Approx const& lhs, double rhs ) {
-            return !operator==( rhs, lhs );
-        }
-
-        friend bool operator <= ( double lhs, Approx const& rhs ) {
-            return lhs < rhs.m_value || lhs == rhs;
-        }
-
-        friend bool operator <= ( Approx const& lhs, double rhs ) {
-            return lhs.m_value < rhs || lhs == rhs;
-        }
-
-        friend bool operator >= ( double lhs, Approx const& rhs ) {
-            return lhs > rhs.m_value || lhs == rhs;
-        }
-
-        friend bool operator >= ( Approx const& lhs, double rhs ) {
-            return lhs.m_value > rhs || lhs == rhs;
-        }
-
-        Approx& epsilon( double newEpsilon ) {
-            m_epsilon = newEpsilon;
-            return *this;
-        }
-
-        Approx& margin( double newMargin ) {
-            m_margin = newMargin;
-            return *this;
-        }
-
-        Approx& scale( double newScale ) {
-            m_scale = newScale;
+        Approx& scale( T const& newScale ) {
+            m_scale = static_cast<double>(newScale);
             return *this;
         }
-#endif
 
-        std::string toString() const {
-            std::ostringstream oss;
-            oss << "Approx( " << Catch::toString( m_value ) << " )";
-            return oss.str();
-        }
+        std::string toString() const;
 
     private:
         double m_epsilon;
@@ -2963,2677 +3174,2330 @@ namespace Detail {
         double m_scale;
         double m_value;
     };
-}
+} // end namespace Detail
+
+namespace literals {
+    Detail::Approx operator "" _a(long double val);
+    Detail::Approx operator "" _a(unsigned long long val);
+} // end namespace literals
 
 template<>
-inline std::string toString<Detail::Approx>( Detail::Approx const& value ) {
-    return value.toString();
-}
+struct StringMaker<Catch::Detail::Approx> {
+    static std::string convert(Catch::Detail::Approx const& value);
+};
 
 } // end namespace Catch
 
-// #included from: internal/catch_matchers_string.h
-#define TWOBLUECUBES_CATCH_MATCHERS_STRING_H_INCLUDED
-
-namespace Catch {
-namespace Matchers {
-
-    namespace StdString {
+// end catch_approx.h
+// start catch_string_manip.h
 
-        struct CasedString
-        {
-            CasedString( std::string const& str, CaseSensitive::Choice caseSensitivity );
-            std::string adjustString( std::string const& str ) const;
-            std::string caseSensitivitySuffix() const;
+#include <string>
+#include <iosfwd>
+#include <vector>
 
-            CaseSensitive::Choice m_caseSensitivity;
-            std::string m_str;
-        };
+namespace Catch {
 
-        struct StringMatcherBase : MatcherBase<std::string> {
-            StringMatcherBase( std::string const& operation, CasedString const& comparator );
-            virtual std::string describe() const CATCH_OVERRIDE;
+    bool startsWith( std::string const& s, std::string const& prefix );
+    bool startsWith( std::string const& s, char prefix );
+    bool endsWith( std::string const& s, std::string const& suffix );
+    bool endsWith( std::string const& s, char suffix );
+    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);
 
-            CasedString m_comparator;
-            std::string m_operation;
-        };
+    // !!! 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 EqualsMatcher : StringMatcherBase {
-            EqualsMatcher( CasedString const& comparator );
-            virtual bool match( std::string const& source ) const CATCH_OVERRIDE;
-        };
-        struct ContainsMatcher : StringMatcherBase {
-            ContainsMatcher( CasedString const& comparator );
-            virtual bool match( std::string const& source ) const CATCH_OVERRIDE;
-        };
-        struct StartsWithMatcher : StringMatcherBase {
-            StartsWithMatcher( CasedString const& comparator );
-            virtual bool match( std::string const& source ) const CATCH_OVERRIDE;
-        };
-        struct EndsWithMatcher : StringMatcherBase {
-            EndsWithMatcher( CasedString const& comparator );
-            virtual bool match( std::string const& source ) const CATCH_OVERRIDE;
-        };
+    struct pluralise {
+        pluralise( std::size_t count, std::string const& label );
 
-    } // namespace StdString
+        friend std::ostream& operator << ( std::ostream& os, pluralise const& pluraliser );
 
-    // The following functions create the actual matcher objects.
-    // This allows the types to be inferred
+        std::size_t m_count;
+        std::string m_label;
+    };
+}
 
-    StdString::EqualsMatcher Equals( std::string const& str, CaseSensitive::Choice caseSensitivity = CaseSensitive::Yes );
-    StdString::ContainsMatcher Contains( std::string const& str, CaseSensitive::Choice caseSensitivity = CaseSensitive::Yes );
-    StdString::EndsWithMatcher EndsWith( std::string const& str, CaseSensitive::Choice caseSensitivity = CaseSensitive::Yes );
-    StdString::StartsWithMatcher StartsWith( std::string const& str, CaseSensitive::Choice caseSensitivity = CaseSensitive::Yes );
+// end catch_string_manip.h
+#ifndef CATCH_CONFIG_DISABLE_MATCHERS
+// start catch_capture_matchers.h
 
-} // namespace Matchers
-} // namespace Catch
+// start catch_matchers.h
 
-// #included from: internal/catch_matchers_vector.h
-#define TWOBLUECUBES_CATCH_MATCHERS_VECTOR_H_INCLUDED
+#include <string>
+#include <vector>
 
 namespace Catch {
 namespace Matchers {
+    namespace Impl {
 
-    namespace Vector {
+        template<typename ArgT> struct MatchAllOf;
+        template<typename ArgT> struct MatchAnyOf;
+        template<typename ArgT> struct MatchNotOf;
 
-        template<typename T>
-        struct ContainsElementMatcher : MatcherBase<std::vector<T>, T> {
+        class MatcherUntypedBase {
+        public:
+            MatcherUntypedBase() = default;
+            MatcherUntypedBase ( MatcherUntypedBase const& ) = default;
+            MatcherUntypedBase& operator = ( MatcherUntypedBase const& ) = delete;
+            std::string toString() const;
 
-            ContainsElementMatcher(T const &comparator) : m_comparator( comparator) {}
+        protected:
+            virtual ~MatcherUntypedBase();
+            virtual std::string describe() const = 0;
+            mutable std::string m_cachedToString;
+        };
 
-            bool match(std::vector<T> const &v) const CATCH_OVERRIDE {
-                return std::find(v.begin(), v.end(), m_comparator) != v.end();
-            }
+#ifdef __clang__
+#    pragma clang diagnostic push
+#    pragma clang diagnostic ignored "-Wnon-virtual-dtor"
+#endif
 
-            virtual std::string describe() const CATCH_OVERRIDE {
-                return "Contains: " + Catch::toString( m_comparator );
-            }
+        template<typename ObjectT>
+        struct MatcherMethod {
+            virtual bool match( ObjectT const& arg ) const = 0;
+        };
 
-            T const& m_comparator;
+#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
 
         template<typename T>
-        struct ContainsMatcher : MatcherBase<std::vector<T>, std::vector<T> > {
+        struct MatcherBase : MatcherUntypedBase, MatcherMethod<T> {
 
-            ContainsMatcher(std::vector<T> const &comparator) : m_comparator( comparator ) {}
+            MatchAllOf<T> operator && ( MatcherBase const& other ) const;
+            MatchAnyOf<T> operator || ( MatcherBase const& other ) const;
+            MatchNotOf<T> operator ! () const;
+        };
 
-            bool match(std::vector<T> const &v) const CATCH_OVERRIDE {
-                // !TBD: see note in EqualsMatcher
-                if (m_comparator.size() > v.size())
-                    return false;
-                for (size_t i = 0; i < m_comparator.size(); ++i)
-                    if (std::find(v.begin(), v.end(), m_comparator[i]) == v.end())
+        template<typename ArgT>
+        struct MatchAllOf : MatcherBase<ArgT> {
+            bool match( ArgT const& arg ) const override {
+                for( auto matcher : m_matchers ) {
+                    if (!matcher->match(arg))
                         return false;
+                }
                 return true;
             }
-            virtual std::string describe() const CATCH_OVERRIDE {
-                return "Contains: " + Catch::toString( m_comparator );
+            std::string describe() const override {
+                std::string description;
+                description.reserve( 4 + m_matchers.size()*32 );
+                description += "( ";
+                bool first = true;
+                for( auto matcher : m_matchers ) {
+                    if( first )
+                        first = false;
+                    else
+                        description += " and ";
+                    description += matcher->toString();
+                }
+                description += " )";
+                return description;
             }
 
-            std::vector<T> const& m_comparator;
-        };
+            MatchAllOf<ArgT> operator && ( MatcherBase<ArgT> const& other ) {
+                auto copy(*this);
+                copy.m_matchers.push_back( &other );
+                return copy;
+            }
 
-        template<typename T>
-        struct EqualsMatcher : MatcherBase<std::vector<T>, std::vector<T> > {
-
-            EqualsMatcher(std::vector<T> const &comparator) : m_comparator( comparator ) {}
+            std::vector<MatcherBase<ArgT> const*> m_matchers;
+        };
+        template<typename ArgT>
+        struct MatchAnyOf : MatcherBase<ArgT> {
 
-            bool match(std::vector<T> const &v) const CATCH_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
-                // - then just call that directly
-                if (m_comparator.size() != v.size())
-                    return false;
-                for (size_t i = 0; i < v.size(); ++i)
-                    if (m_comparator[i] != v[i])
-                        return false;
-                return true;
+            bool match( ArgT const& arg ) const override {
+                for( auto matcher : m_matchers ) {
+                    if (matcher->match(arg))
+                        return true;
+                }
+                return false;
             }
-            virtual std::string describe() const CATCH_OVERRIDE {
-                return "Equals: " + Catch::toString( m_comparator );
+            std::string describe() const override {
+                std::string description;
+                description.reserve( 4 + m_matchers.size()*32 );
+                description += "( ";
+                bool first = true;
+                for( auto matcher : m_matchers ) {
+                    if( first )
+                        first = false;
+                    else
+                        description += " or ";
+                    description += matcher->toString();
+                }
+                description += " )";
+                return description;
             }
-            std::vector<T> const& m_comparator;
-        };
 
-    } // namespace Vector
-
-    // The following functions create the actual matcher objects.
-    // This allows the types to be inferred
+            MatchAnyOf<ArgT> operator || ( MatcherBase<ArgT> const& other ) {
+                auto copy(*this);
+                copy.m_matchers.push_back( &other );
+                return copy;
+            }
 
-    template<typename T>
-    Vector::ContainsMatcher<T> Contains( std::vector<T> const& comparator ) {
-        return Vector::ContainsMatcher<T>( comparator );
-    }
+            std::vector<MatcherBase<ArgT> const*> m_matchers;
+        };
 
-    template<typename T>
-    Vector::ContainsElementMatcher<T> VectorContains( T const& comparator ) {
-        return Vector::ContainsElementMatcher<T>( comparator );
-    }
+        template<typename ArgT>
+        struct MatchNotOf : MatcherBase<ArgT> {
 
-    template<typename T>
-    Vector::EqualsMatcher<T> Equals( std::vector<T> const& comparator ) {
-        return Vector::EqualsMatcher<T>( comparator );
-    }
+            MatchNotOf( MatcherBase<ArgT> const& underlyingMatcher ) : m_underlyingMatcher( underlyingMatcher ) {}
 
-} // namespace Matchers
-} // namespace Catch
+            bool match( ArgT const& arg ) const override {
+                return !m_underlyingMatcher.match( arg );
+            }
 
-// #included from: internal/catch_interfaces_tag_alias_registry.h
-#define TWOBLUECUBES_CATCH_INTERFACES_TAG_ALIAS_REGISTRY_H_INCLUDED
+            std::string describe() const override {
+                return "not " + m_underlyingMatcher.toString();
+            }
+            MatcherBase<ArgT> const& m_underlyingMatcher;
+        };
 
-// #included from: catch_tag_alias.h
-#define TWOBLUECUBES_CATCH_TAG_ALIAS_H_INCLUDED
+        template<typename T>
+        MatchAllOf<T> MatcherBase<T>::operator && ( MatcherBase const& other ) const {
+            return MatchAllOf<T>() && *this && other;
+        }
+        template<typename T>
+        MatchAnyOf<T> MatcherBase<T>::operator || ( MatcherBase const& other ) const {
+            return MatchAnyOf<T>() || *this || other;
+        }
+        template<typename T>
+        MatchNotOf<T> MatcherBase<T>::operator ! () const {
+            return MatchNotOf<T>( *this );
+        }
 
-#include <string>
+    } // namespace Impl
 
-namespace Catch {
+} // namespace Matchers
 
-    struct TagAlias {
-        TagAlias( std::string const& _tag, SourceLineInfo _lineInfo ) : tag( _tag ), lineInfo( _lineInfo ) {}
+using namespace Matchers;
+using Matchers::Impl::MatcherBase;
 
-        std::string tag;
-        SourceLineInfo lineInfo;
-    };
+} // namespace Catch
 
-    struct RegistrarForTagAliases {
-        RegistrarForTagAliases( char const* alias, char const* tag, SourceLineInfo const& lineInfo );
-    };
+// end catch_matchers.h
+// start catch_matchers_exception.hpp
 
-} // end namespace Catch
+namespace Catch {
+namespace Matchers {
+namespace Exception {
 
-#define CATCH_REGISTER_TAG_ALIAS( alias, spec ) namespace{ Catch::RegistrarForTagAliases INTERNAL_CATCH_UNIQUE_NAME( AutoRegisterTagAlias )( alias, spec, CATCH_INTERNAL_LINEINFO ); }
-// #included from: catch_option.hpp
-#define TWOBLUECUBES_CATCH_OPTION_HPP_INCLUDED
+class ExceptionMessageMatcher : public MatcherBase<std::exception> {
+    std::string m_message;
+public:
 
-namespace Catch {
+    ExceptionMessageMatcher(std::string const& message):
+        m_message(message)
+    {}
 
-    // An optional type
-    template<typename T>
-    class Option {
-    public:
-        Option() : nullableValue( CATCH_NULL ) {}
-        Option( T const& _value )
-        : nullableValue( new( storage ) T( _value ) )
-        {}
-        Option( Option const& _other )
-        : nullableValue( _other ? new( storage ) T( *_other ) : CATCH_NULL )
-        {}
+    bool match(std::exception const& ex) const override;
 
-        ~Option() {
-            reset();
-        }
+    std::string describe() const override;
+};
 
-        Option& operator= ( Option const& _other ) {
-            if( &_other != this ) {
-                reset();
-                if( _other )
-                    nullableValue = new( storage ) T( *_other );
-            }
-            return *this;
-        }
-        Option& operator = ( T const& _value ) {
-            reset();
-            nullableValue = new( storage ) T( _value );
-            return *this;
-        }
+} // namespace Exception
 
-        void reset() {
-            if( nullableValue )
-                nullableValue->~T();
-            nullableValue = CATCH_NULL;
-        }
+Exception::ExceptionMessageMatcher Message(std::string const& message);
 
-        T& operator*() { return *nullableValue; }
-        T const& operator*() const { return *nullableValue; }
-        T* operator->() { return nullableValue; }
-        const T* operator->() const { return nullableValue; }
+} // namespace Matchers
+} // namespace Catch
 
-        T valueOr( T const& defaultValue ) const {
-            return nullableValue ? *nullableValue : defaultValue;
-        }
+// end catch_matchers_exception.hpp
+// start catch_matchers_floating.h
 
-        bool some() const { return nullableValue != CATCH_NULL; }
-        bool none() const { return nullableValue == CATCH_NULL; }
+namespace Catch {
+namespace Matchers {
 
-        bool operator !() const { return nullableValue == CATCH_NULL; }
-        operator SafeBool::type() const {
-            return SafeBool::makeSafe( some() );
-        }
+    namespace Floating {
 
-    private:
-        T *nullableValue;
-        union {
-            char storage[sizeof(T)];
+        enum class FloatingPointKind : uint8_t;
 
-            // These are here to force alignment for the storage
-            long double dummy1;
-            void (*dummy2)();
-            long double dummy3;
-#ifdef CATCH_CONFIG_CPP11_LONG_LONG
-            long long dummy4;
-#endif
+        struct WithinAbsMatcher : MatcherBase<double> {
+            WithinAbsMatcher(double target, double margin);
+            bool match(double const& matchee) const override;
+            std::string describe() const override;
+        private:
+            double m_target;
+            double m_margin;
         };
-    };
 
-} // end namespace Catch
+        struct WithinUlpsMatcher : MatcherBase<double> {
+            WithinUlpsMatcher(double target, uint64_t ulps, FloatingPointKind baseType);
+            bool match(double const& matchee) const override;
+            std::string describe() const override;
+        private:
+            double m_target;
+            uint64_t m_ulps;
+            FloatingPointKind m_type;
+        };
 
-namespace Catch {
+        // 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;
+        };
 
-    struct ITagAliasRegistry {
-        virtual ~ITagAliasRegistry();
-        virtual Option<TagAlias> find( std::string const& alias ) const = 0;
-        virtual std::string expandAliases( std::string const& unexpandedTestSpec ) const = 0;
+    } // namespace Floating
 
-        static ITagAliasRegistry const& get();
-    };
+    // The following functions create the actual matcher objects.
+    // This allows the types to be inferred
+    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);
 
-} // end namespace Catch
+} // namespace Matchers
+} // namespace Catch
 
-// These files are included here so the single_include script doesn't put them
-// in the conditionally compiled sections
-// #included from: internal/catch_test_case_info.h
-#define TWOBLUECUBES_CATCH_TEST_CASE_INFO_H_INCLUDED
+// end catch_matchers_floating.h
+// start catch_matchers_generic.hpp
 
+#include <functional>
 #include <string>
-#include <set>
-
-#ifdef __clang__
-#pragma clang diagnostic push
-#pragma clang diagnostic ignored "-Wpadded"
-#endif
 
 namespace Catch {
+namespace Matchers {
+namespace Generic {
 
-    struct ITestCase;
+namespace Detail {
+    std::string finalizeDescription(const std::string& desc);
+}
 
-    struct TestCaseInfo {
-        enum SpecialProperties{
-            None = 0,
-            IsHidden = 1 << 1,
-            ShouldFail = 1 << 2,
-            MayFail = 1 << 3,
-            Throws = 1 << 4,
-            NonPortable = 1 << 5
-        };
+template <typename T>
+class PredicateMatcher : public MatcherBase<T> {
+    std::function<bool(T const&)> m_predicate;
+    std::string m_description;
+public:
 
-        TestCaseInfo(   std::string const& _name,
-                        std::string const& _className,
-                        std::string const& _description,
-                        std::set<std::string> const& _tags,
-                        SourceLineInfo const& _lineInfo );
+    PredicateMatcher(std::function<bool(T const&)> const& elem, std::string const& descr)
+        :m_predicate(std::move(elem)),
+        m_description(Detail::finalizeDescription(descr))
+    {}
 
-        TestCaseInfo( TestCaseInfo const& other );
+    bool match( T const& item ) const override {
+        return m_predicate(item);
+    }
 
-        friend void setTags( TestCaseInfo& testCaseInfo, std::set<std::string> const& tags );
+    std::string describe() const override {
+        return m_description;
+    }
+};
 
-        bool isHidden() const;
-        bool throws() const;
-        bool okToFail() const;
-        bool expectedToFail() const;
+} // namespace Generic
 
-        std::string name;
-        std::string className;
-        std::string description;
-        std::set<std::string> tags;
-        std::set<std::string> lcaseTags;
-        std::string tagsAsString;
-        SourceLineInfo lineInfo;
-        SpecialProperties properties;
-    };
+    // The following functions create the actual matcher objects.
+    // The user has to explicitly specify type to the function, because
+    // 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 = "") {
+        return Generic::PredicateMatcher<T>(predicate, description);
+    }
 
-    class TestCase : public TestCaseInfo {
-    public:
+} // namespace Matchers
+} // namespace Catch
 
-        TestCase( ITestCase* testCase, TestCaseInfo const& info );
-        TestCase( TestCase const& other );
+// end catch_matchers_generic.hpp
+// start catch_matchers_string.h
 
-        TestCase withName( std::string const& _newName ) const;
+#include <string>
 
-        void invoke() const;
+namespace Catch {
+namespace Matchers {
 
-        TestCaseInfo const& getTestCaseInfo() const;
+    namespace StdString {
 
-        void swap( TestCase& other );
-        bool operator == ( TestCase const& other ) const;
-        bool operator < ( TestCase const& other ) const;
-        TestCase& operator = ( TestCase const& other );
+        struct CasedString
+        {
+            CasedString( std::string const& str, CaseSensitive::Choice caseSensitivity );
+            std::string adjustString( std::string const& str ) const;
+            std::string caseSensitivitySuffix() const;
 
-    private:
-        Ptr<ITestCase> test;
-    };
+            CaseSensitive::Choice m_caseSensitivity;
+            std::string m_str;
+        };
 
-    TestCase makeTestCase(  ITestCase* testCase,
-                            std::string const& className,
-                            std::string const& name,
-                            std::string const& description,
-                            SourceLineInfo const& lineInfo );
-}
+        struct StringMatcherBase : MatcherBase<std::string> {
+            StringMatcherBase( std::string const& operation, CasedString const& comparator );
+            std::string describe() const override;
 
-#ifdef __clang__
-#pragma clang diagnostic pop
-#endif
+            CasedString m_comparator;
+            std::string m_operation;
+        };
 
+        struct EqualsMatcher : StringMatcherBase {
+            EqualsMatcher( CasedString const& comparator );
+            bool match( std::string const& source ) const override;
+        };
+        struct ContainsMatcher : StringMatcherBase {
+            ContainsMatcher( CasedString const& comparator );
+            bool match( std::string const& source ) const override;
+        };
+        struct StartsWithMatcher : StringMatcherBase {
+            StartsWithMatcher( CasedString const& comparator );
+            bool match( std::string const& source ) const override;
+        };
+        struct EndsWithMatcher : StringMatcherBase {
+            EndsWithMatcher( CasedString const& comparator );
+            bool match( std::string const& source ) const override;
+        };
 
-#ifdef __OBJC__
-// #included from: internal/catch_objc.hpp
-#define TWOBLUECUBES_CATCH_OBJC_HPP_INCLUDED
+        struct RegexMatcher : MatcherBase<std::string> {
+            RegexMatcher( std::string regex, CaseSensitive::Choice caseSensitivity );
+            bool match( std::string const& matchee ) const override;
+            std::string describe() const override;
 
-#import <objc/runtime.h>
+        private:
+            std::string m_regex;
+            CaseSensitive::Choice m_caseSensitivity;
+        };
 
-#include <string>
+    } // namespace StdString
 
-// NB. Any general catch headers included here must be included
-// in catch.hpp first to make sure they are included by the single
-// header for non obj-usage
+    // The following functions create the actual matcher objects.
+    // This allows the types to be inferred
 
-///////////////////////////////////////////////////////////////////////////////
-// This protocol is really only here for (self) documenting purposes, since
-// all its methods are optional.
-@protocol OcFixture
+    StdString::EqualsMatcher Equals( std::string const& str, CaseSensitive::Choice caseSensitivity = CaseSensitive::Yes );
+    StdString::ContainsMatcher Contains( std::string const& str, CaseSensitive::Choice caseSensitivity = CaseSensitive::Yes );
+    StdString::EndsWithMatcher EndsWith( std::string const& str, CaseSensitive::Choice caseSensitivity = CaseSensitive::Yes );
+    StdString::StartsWithMatcher StartsWith( std::string const& str, CaseSensitive::Choice caseSensitivity = CaseSensitive::Yes );
+    StdString::RegexMatcher Matches( std::string const& regex, CaseSensitive::Choice caseSensitivity = CaseSensitive::Yes );
 
-@optional
+} // namespace Matchers
+} // namespace Catch
 
--(void) setUp;
--(void) tearDown;
+// end catch_matchers_string.h
+// start catch_matchers_vector.h
 
-@end
+#include <algorithm>
 
 namespace Catch {
+namespace Matchers {
 
-    class OcMethod : public SharedImpl<ITestCase> {
-
-    public:
-        OcMethod( Class cls, SEL sel ) : m_cls( cls ), m_sel( sel ) {}
-
-        virtual void invoke() const {
-            id obj = [[m_cls alloc] init];
-
-            performOptionalSelector( obj, @selector(setUp)  );
-            performOptionalSelector( obj, m_sel );
-            performOptionalSelector( obj, @selector(tearDown)  );
-
-            arcSafeRelease( obj );
-        }
-    private:
-        virtual ~OcMethod() {}
+    namespace Vector {
+        template<typename T, typename Alloc>
+        struct ContainsElementMatcher : MatcherBase<std::vector<T, Alloc>> {
 
-        Class m_cls;
-        SEL m_sel;
-    };
+            ContainsElementMatcher(T const &comparator) : m_comparator( comparator) {}
 
-    namespace Detail{
+            bool match(std::vector<T, Alloc> const &v) const override {
+                for (auto const& el : v) {
+                    if (el == m_comparator) {
+                        return true;
+                    }
+                }
+                return false;
+            }
 
-        inline std::string getAnnotation(   Class cls,
-                                            std::string const& annotationName,
-                                            std::string const& testCaseName ) {
-            NSString* selStr = [[NSString alloc] initWithFormat:@"Catch_%s_%s", annotationName.c_str(), testCaseName.c_str()];
-            SEL sel = NSSelectorFromString( selStr );
-            arcSafeRelease( selStr );
-            id value = performOptionalSelector( cls, sel );
-            if( value )
-                return [(NSString*)value UTF8String];
-            return "";
-        }
-    }
+            std::string describe() const override {
+                return "Contains: " + ::Catch::Detail::stringify( m_comparator );
+            }
 
-    inline size_t registerTestMethods() {
-        size_t noTestMethods = 0;
-        int noClasses = objc_getClassList( CATCH_NULL, 0 );
+            T const& m_comparator;
+        };
 
-        Class* classes = (CATCH_UNSAFE_UNRETAINED Class *)malloc( sizeof(Class) * noClasses);
-        objc_getClassList( classes, noClasses );
+        template<typename T, typename AllocComp, typename AllocMatch>
+        struct ContainsMatcher : MatcherBase<std::vector<T, AllocMatch>> {
 
-        for( int c = 0; c < noClasses; c++ ) {
-            Class cls = classes[c];
-            {
-                u_int count;
-                Method* methods = class_copyMethodList( cls, &count );
-                for( u_int m = 0; m < count ; m++ ) {
-                    SEL selector = method_getName(methods[m]);
-                    std::string methodName = sel_getName(selector);
-                    if( startsWith( methodName, "Catch_TestCase_" ) ) {
-                        std::string testCaseName = methodName.substr( 15 );
-                        std::string name = Detail::getAnnotation( cls, "Name", testCaseName );
-                        std::string desc = Detail::getAnnotation( cls, "Description", testCaseName );
-                        const char* className = class_getName( cls );
+            ContainsMatcher(std::vector<T, AllocComp> const &comparator) : m_comparator( comparator ) {}
 
-                        getMutableRegistryHub().registerTest( makeTestCase( new OcMethod( cls, selector ), className, name.c_str(), desc.c_str(), SourceLineInfo() ) );
-                        noTestMethods++;
+            bool match(std::vector<T, AllocMatch> const &v) const override {
+                // !TBD: see note in EqualsMatcher
+                if (m_comparator.size() > v.size())
+                    return false;
+                for (auto const& comparator : m_comparator) {
+                    auto present = false;
+                    for (const auto& el : v) {
+                        if (el == comparator) {
+                            present = true;
+                            break;
+                        }
+                    }
+                    if (!present) {
+                        return false;
                     }
                 }
-                free(methods);
+                return true;
+            }
+            std::string describe() const override {
+                return "Contains: " + ::Catch::Detail::stringify( m_comparator );
             }
-        }
-        return noTestMethods;
-    }
 
-    namespace Matchers {
-        namespace Impl {
-        namespace NSStringMatchers {
+            std::vector<T, AllocComp> const& m_comparator;
+        };
 
-            struct StringHolder : MatcherBase<NSString*>{
-                StringHolder( NSString* substr ) : m_substr( [substr copy] ){}
-                StringHolder( StringHolder const& other ) : m_substr( [other.m_substr copy] ){}
-                StringHolder() {
-                    arcSafeRelease( m_substr );
-                }
+        template<typename T, typename AllocComp, typename AllocMatch>
+        struct EqualsMatcher : MatcherBase<std::vector<T, AllocMatch>> {
+
+            EqualsMatcher(std::vector<T, AllocComp> const &comparator) : m_comparator( comparator ) {}
 
-                virtual bool match( NSString* arg ) const CATCH_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, Alloc> etc
+                // - then just call that directly
+                if (m_comparator.size() != v.size())
                     return false;
-                }
+                for (std::size_t i = 0; i < v.size(); ++i)
+                    if (m_comparator[i] != v[i])
+                        return false;
+                return true;
+            }
+            std::string describe() const override {
+                return "Equals: " + ::Catch::Detail::stringify( m_comparator );
+            }
+            std::vector<T, AllocComp> const& m_comparator;
+        };
 
-                NSString* m_substr;
-            };
+        template<typename T, typename AllocComp, typename AllocMatch>
+        struct ApproxMatcher : MatcherBase<std::vector<T, AllocMatch>> {
 
-            struct Equals : StringHolder {
-                Equals( NSString* substr ) : StringHolder( substr ){}
+            ApproxMatcher(std::vector<T, AllocComp> const& comparator) : m_comparator( comparator ) {}
 
-                virtual bool match( NSString* str ) const CATCH_OVERRIDE {
-                    return  (str != nil || m_substr == nil ) &&
-                            [str isEqualToString:m_substr];
-                }
+            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();
+        };
 
-                virtual std::string describe() const CATCH_OVERRIDE {
-                    return "equals string: " + Catch::toString( m_substr );
+        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());
+            }
 
-            struct Contains : StringHolder {
-                Contains( NSString* substr ) : StringHolder( substr ){}
+            std::string describe() const override {
+                return "UnorderedEquals: " + ::Catch::Detail::stringify(m_target);
+            }
+        private:
+            std::vector<T, AllocComp> const& m_target;
+        };
 
-                virtual bool match( NSString* str ) const {
-                    return  (str != nil || m_substr == nil ) &&
-                            [str rangeOfString:m_substr].location != NSNotFound;
-                }
+    } // namespace Vector
 
-                virtual std::string describe() const CATCH_OVERRIDE {
-                    return "contains string: " + Catch::toString( m_substr );
-                }
-            };
+    // The following functions create the actual matcher objects.
+    // This allows the types to be inferred
 
-            struct StartsWith : StringHolder {
-                StartsWith( NSString* substr ) : StringHolder( substr ){}
+    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 );
+    }
 
-                virtual bool match( NSString* str ) const {
-                    return  (str != nil || m_substr == nil ) &&
-                            [str rangeOfString:m_substr].location == 0;
-                }
+    template<typename T, typename Alloc = std::allocator<T>>
+    Vector::ContainsElementMatcher<T, Alloc> VectorContains( T const& comparator ) {
+        return Vector::ContainsElementMatcher<T, Alloc>( comparator );
+    }
 
-                virtual std::string describe() const CATCH_OVERRIDE {
-                    return "starts with: " + Catch::toString( m_substr );
-                }
-            };
-            struct EndsWith : StringHolder {
-                EndsWith( NSString* substr ) : StringHolder( substr ){}
+    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 );
+    }
 
-                virtual bool match( NSString* str ) const {
-                    return  (str != nil || m_substr == nil ) &&
-                            [str rangeOfString:m_substr].location == [str length] - [m_substr length];
-                }
+    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 );
+    }
 
-                virtual std::string describe() const CATCH_OVERRIDE {
-                    return "ends with: " + Catch::toString( m_substr );
-                }
-            };
+    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 NSStringMatchers
-        } // namespace Impl
+} // namespace Matchers
+} // namespace Catch
 
-        inline Impl::NSStringMatchers::Equals
-            Equals( NSString* substr ){ return Impl::NSStringMatchers::Equals( substr ); }
+// end catch_matchers_vector.h
+namespace Catch {
 
-        inline Impl::NSStringMatchers::Contains
-            Contains( NSString* substr ){ return Impl::NSStringMatchers::Contains( substr ); }
+    template<typename ArgT, typename MatcherT>
+    class MatchExpr : public ITransientExpression {
+        ArgT const& m_arg;
+        MatcherT m_matcher;
+        StringRef m_matcherString;
+    public:
+        MatchExpr( ArgT const& arg, MatcherT const& matcher, StringRef const& matcherString )
+        :   ITransientExpression{ true, matcher.match( arg ) },
+            m_arg( arg ),
+            m_matcher( matcher ),
+            m_matcherString( matcherString )
+        {}
 
-        inline Impl::NSStringMatchers::StartsWith
-            StartsWith( NSString* substr ){ return Impl::NSStringMatchers::StartsWith( substr ); }
+        void streamReconstructedExpression( std::ostream &os ) const override {
+            auto matcherAsString = m_matcher.toString();
+            os << Catch::Detail::stringify( m_arg ) << ' ';
+            if( matcherAsString == Detail::unprintableString )
+                os << m_matcherString;
+            else
+                os << matcherAsString;
+        }
+    };
 
-        inline Impl::NSStringMatchers::EndsWith
-            EndsWith( NSString* substr ){ return Impl::NSStringMatchers::EndsWith( substr ); }
+    using StringMatcher = Matchers::Impl::MatcherBase<std::string>;
 
-    } // namespace Matchers
+    void handleExceptionMatchExpr( AssertionHandler& handler, StringMatcher const& matcher, StringRef const& matcherString  );
 
-    using namespace Matchers;
+    template<typename ArgT, typename MatcherT>
+    auto makeMatchExpr( ArgT const& arg, MatcherT const& matcher, StringRef const& matcherString  ) -> MatchExpr<ArgT, MatcherT> {
+        return MatchExpr<ArgT, MatcherT>( arg, matcher, matcherString );
+    }
 
 } // namespace Catch
 
 ///////////////////////////////////////////////////////////////////////////////
-#define OC_TEST_CASE( name, desc )\
-+(NSString*) INTERNAL_CATCH_UNIQUE_NAME( Catch_Name_test ) \
-{\
-return @ name; \
-}\
-+(NSString*) INTERNAL_CATCH_UNIQUE_NAME( Catch_Description_test ) \
-{ \
-return @ desc; \
-} \
--(void) INTERNAL_CATCH_UNIQUE_NAME( Catch_TestCase_test )
-
-#endif
+#define INTERNAL_CHECK_THAT( macroName, matcher, resultDisposition, arg ) \
+    do { \
+        Catch::AssertionHandler catchAssertionHandler( macroName##_catch_sr, CATCH_INTERNAL_LINEINFO, CATCH_INTERNAL_STRINGIFY(arg) ", " CATCH_INTERNAL_STRINGIFY(matcher), resultDisposition ); \
+        INTERNAL_CATCH_TRY { \
+            catchAssertionHandler.handleExpr( Catch::makeMatchExpr( arg, matcher, #matcher##_catch_sr ) ); \
+        } INTERNAL_CATCH_CATCH( catchAssertionHandler ) \
+        INTERNAL_CATCH_REACT( catchAssertionHandler ) \
+    } while( false )
 
-#ifdef CATCH_IMPL
+///////////////////////////////////////////////////////////////////////////////
+#define INTERNAL_CATCH_THROWS_MATCHES( macroName, exceptionType, resultDisposition, matcher, ... ) \
+    do { \
+        Catch::AssertionHandler catchAssertionHandler( macroName##_catch_sr, CATCH_INTERNAL_LINEINFO, CATCH_INTERNAL_STRINGIFY(__VA_ARGS__) ", " CATCH_INTERNAL_STRINGIFY(exceptionType) ", " CATCH_INTERNAL_STRINGIFY(matcher), resultDisposition ); \
+        if( catchAssertionHandler.allowThrows() ) \
+            try { \
+                static_cast<void>(__VA_ARGS__ ); \
+                catchAssertionHandler.handleUnexpectedExceptionNotThrown(); \
+            } \
+            catch( exceptionType const& ex ) { \
+                catchAssertionHandler.handleExpr( Catch::makeMatchExpr( ex, matcher, #matcher##_catch_sr ) ); \
+            } \
+            catch( ... ) { \
+                catchAssertionHandler.handleUnexpectedInflightException(); \
+            } \
+        else \
+            catchAssertionHandler.handleThrowingCallSkipped(); \
+        INTERNAL_CATCH_REACT( catchAssertionHandler ) \
+    } while( false )
 
-// !TBD: Move the leak detector code into a separate header
-#ifdef CATCH_CONFIG_WINDOWS_CRTDBG
-#include <crtdbg.h>
-class LeakDetector {
-public:
-    LeakDetector() {
-        int flag = _CrtSetDbgFlag(_CRTDBG_REPORT_FLAG);
-        flag |= _CRTDBG_LEAK_CHECK_DF;
-        flag |= _CRTDBG_ALLOC_MEM_DF;
-        _CrtSetDbgFlag(flag);
-        _CrtSetReportMode(_CRT_WARN, _CRTDBG_MODE_FILE | _CRTDBG_MODE_DEBUG);
-        _CrtSetReportFile(_CRT_WARN, _CRTDBG_FILE_STDERR);
-        // Change this to leaking allocation's number to break there
-        _CrtSetBreakAlloc(-1);
-    }
-};
-#else
-class LeakDetector {};
+// end catch_capture_matchers.h
 #endif
+// start catch_generators.hpp
 
-LeakDetector leakDetector;
+// start catch_interfaces_generatortracker.h
 
-// #included from: internal/catch_impl.hpp
-#define TWOBLUECUBES_CATCH_IMPL_HPP_INCLUDED
 
-// Collect all the implementation files together here
-// These are the equivalent of what would usually be cpp files
+#include <memory>
 
-#ifdef __clang__
-#pragma clang diagnostic push
-#pragma clang diagnostic ignored "-Wweak-vtables"
-#endif
+namespace Catch {
 
-// #included from: ../catch_session.hpp
-#define TWOBLUECUBES_CATCH_RUNNER_HPP_INCLUDED
+    namespace Generators {
+        class GeneratorUntypedBase {
+        public:
+            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<GeneratorUntypedBase>;
 
-// #included from: internal/catch_commandline.hpp
-#define TWOBLUECUBES_CATCH_COMMANDLINE_HPP_INCLUDED
+    } // namespace Generators
 
-// #included from: catch_config.hpp
-#define TWOBLUECUBES_CATCH_CONFIG_HPP_INCLUDED
+    struct IGeneratorTracker {
+        virtual ~IGeneratorTracker();
+        virtual auto hasGenerator() const -> bool = 0;
+        virtual auto getGenerator() const -> Generators::GeneratorBasePtr const& = 0;
+        virtual void setGenerator( Generators::GeneratorBasePtr&& generator ) = 0;
+    };
 
-// #included from: catch_test_spec_parser.hpp
-#define TWOBLUECUBES_CATCH_TEST_SPEC_PARSER_HPP_INCLUDED
+} // namespace Catch
 
-#ifdef __clang__
-#pragma clang diagnostic push
-#pragma clang diagnostic ignored "-Wpadded"
-#endif
+// end catch_interfaces_generatortracker.h
+// start catch_enforce.h
 
-// #included from: catch_test_spec.hpp
-#define TWOBLUECUBES_CATCH_TEST_SPEC_HPP_INCLUDED
+#include <exception>
 
-#ifdef __clang__
-#pragma clang diagnostic push
-#pragma clang diagnostic ignored "-Wpadded"
+namespace Catch {
+#if !defined(CATCH_CONFIG_DISABLE_EXCEPTIONS)
+    template <typename Ex>
+    [[noreturn]]
+    void throw_exception(Ex const& e) {
+        throw e;
+    }
+#else // ^^ Exceptions are enabled //  Exceptions are disabled vv
+    [[noreturn]]
+    void throw_exception(std::exception const& e);
 #endif
 
-// #included from: catch_wildcard_pattern.hpp
-#define TWOBLUECUBES_CATCH_WILDCARD_PATTERN_HPP_INCLUDED
+    [[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);
 
-#include <stdexcept>
+} // namespace Catch;
 
-namespace Catch
-{
-    class WildcardPattern {
-        enum WildcardPosition {
-            NoWildcard = 0,
-            WildcardAtStart = 1,
-            WildcardAtEnd = 2,
-            WildcardAtBothEnds = WildcardAtStart | WildcardAtEnd
-        };
+#define CATCH_MAKE_MSG(...) \
+    (Catch::ReusableStringStream() << __VA_ARGS__).str()
 
-    public:
+#define CATCH_INTERNAL_ERROR(...) \
+    Catch::throw_logic_error(CATCH_MAKE_MSG( CATCH_INTERNAL_LINEINFO << ": Internal Catch2 error: " << __VA_ARGS__))
 
-        WildcardPattern( std::string const& pattern, CaseSensitive::Choice caseSensitivity )
-        :   m_caseSensitivity( caseSensitivity ),
-            m_wildcard( NoWildcard ),
-            m_pattern( adjustCase( pattern ) )
-        {
-            if( startsWith( m_pattern, '*' ) ) {
-                m_pattern = m_pattern.substr( 1 );
-                m_wildcard = WildcardAtStart;
-            }
-            if( endsWith( m_pattern, '*' ) ) {
-                m_pattern = m_pattern.substr( 0, m_pattern.size()-1 );
-                m_wildcard = static_cast<WildcardPosition>( m_wildcard | WildcardAtEnd );
-            }
-        }
-        virtual ~WildcardPattern();
-        virtual bool matches( std::string const& str ) const {
-            switch( m_wildcard ) {
-                case NoWildcard:
-                    return m_pattern == adjustCase( str );
-                case WildcardAtStart:
-                    return endsWith( adjustCase( str ), m_pattern );
-                case WildcardAtEnd:
-                    return startsWith( adjustCase( str ), m_pattern );
-                case WildcardAtBothEnds:
-                    return contains( adjustCase( str ), m_pattern );
-            }
+#define CATCH_ERROR(...) \
+    Catch::throw_domain_error(CATCH_MAKE_MSG( __VA_ARGS__ ))
 
-#ifdef __clang__
-#pragma clang diagnostic push
-#pragma clang diagnostic ignored "-Wunreachable-code"
-#endif
-            throw std::logic_error( "Unknown enum" );
-#ifdef __clang__
-#pragma clang diagnostic pop
-#endif
-        }
-    private:
-        std::string adjustCase( std::string const& str ) const {
-            return m_caseSensitivity == CaseSensitive::No ? toLower( str ) : str;
-        }
-        CaseSensitive::Choice m_caseSensitivity;
-        WildcardPosition m_wildcard;
-        std::string m_pattern;
-    };
-}
+#define CATCH_RUNTIME_ERROR(...) \
+    Catch::throw_runtime_error(CATCH_MAKE_MSG( __VA_ARGS__ ))
 
-#include <string>
+#define CATCH_ENFORCE( condition, ... ) \
+    do{ if( !(condition) ) CATCH_ERROR( __VA_ARGS__ ); } while(false)
+
+// end catch_enforce.h
+#include <memory>
 #include <vector>
+#include <cassert>
+
+#include <utility>
+#include <exception>
 
 namespace Catch {
 
-    class TestSpec {
-        struct Pattern : SharedImpl<> {
-            virtual ~Pattern();
-            virtual bool matches( TestCaseInfo const& testCase ) const = 0;
-        };
-        class NamePattern : public Pattern {
-        public:
-            NamePattern( std::string const& name )
-            : m_wildcardPattern( toLower( name ), CaseSensitive::No )
-            {}
-            virtual ~NamePattern();
-            virtual bool matches( TestCaseInfo const& testCase ) const {
-                return m_wildcardPattern.matches( toLower( testCase.name ) );
-            }
-        private:
-            WildcardPattern m_wildcardPattern;
-        };
+class GeneratorException : public std::exception {
+    const char* const m_msg = "";
 
-        class TagPattern : public Pattern {
-        public:
-            TagPattern( std::string const& tag ) : m_tag( toLower( tag ) ) {}
-            virtual ~TagPattern();
-            virtual bool matches( TestCaseInfo const& testCase ) const {
-                return testCase.lcaseTags.find( m_tag ) != testCase.lcaseTags.end();
-            }
-        private:
-            std::string m_tag;
-        };
+public:
+    GeneratorException(const char* msg):
+        m_msg(msg)
+    {}
 
-        class ExcludedPattern : public Pattern {
-        public:
-            ExcludedPattern( Ptr<Pattern> const& underlyingPattern ) : m_underlyingPattern( underlyingPattern ) {}
-            virtual ~ExcludedPattern();
-            virtual bool matches( TestCaseInfo const& testCase ) const { return !m_underlyingPattern->matches( testCase ); }
-        private:
-            Ptr<Pattern> m_underlyingPattern;
-        };
+    const char* what() const noexcept override final;
+};
 
-        struct Filter {
-            std::vector<Ptr<Pattern> > m_patterns;
+namespace Generators {
 
-            bool matches( TestCaseInfo const& testCase ) const {
-                // All patterns in a filter must match for the filter to be a match
-                for( std::vector<Ptr<Pattern> >::const_iterator it = m_patterns.begin(), itEnd = m_patterns.end(); it != itEnd; ++it ) {
-                    if( !(*it)->matches( testCase ) )
-                        return false;
-                }
-                return true;
-            }
-        };
+    // !TBD move this into its own location?
+    namespace pf{
+        template<typename T, typename... Args>
+        std::unique_ptr<T> make_unique( Args&&... args ) {
+            return std::unique_ptr<T>(new T(std::forward<Args>(args)...));
+        }
+    }
 
+    template<typename T>
+    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 final : public IGenerator<T> {
+        T m_value;
     public:
-        bool hasFilters() const {
-            return !m_filters.empty();
+        SingleValueGenerator(T&& value) : m_value(std::move(value)) {}
+
+        T const& get() const override {
+            return m_value;
         }
-        bool matches( TestCaseInfo const& testCase ) const {
-            // A TestSpec matches if any filter matches
-            for( std::vector<Filter>::const_iterator it = m_filters.begin(), itEnd = m_filters.end(); it != itEnd; ++it )
-                if( it->matches( testCase ) )
-                    return true;
+        bool next() override {
             return false;
         }
+    };
 
-    private:
-        std::vector<Filter> m_filters;
+    template<typename 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 ) {}
 
-        friend class TestSpecParser;
+        T const& get() const override {
+            return m_values[m_idx];
+        }
+        bool next() override {
+            ++m_idx;
+            return m_idx < m_values.size();
+        }
     };
-}
 
-#ifdef __clang__
-#pragma clang diagnostic pop
-#endif
+    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();
+        }
+    };
 
-namespace Catch {
+    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));
+    }
 
-    class TestSpecParser {
-        enum Mode{ None, Name, QuotedName, Tag, EscapedName };
-        Mode m_mode;
-        bool m_exclusion;
-        std::size_t m_start, m_pos;
-        std::string m_arg;
-        std::vector<std::size_t> m_escapeChars;
-        TestSpec::Filter m_currentFilter;
-        TestSpec m_testSpec;
-        ITagAliasRegistry const* m_tagAliases;
+    template<typename T>
+    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:
-        TestSpecParser( ITagAliasRegistry const& tagAliases ) :m_mode(None), m_exclusion(false), m_start(0), m_pos(0), m_tagAliases( &tagAliases ) {}
-
-        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();
-            for( m_pos = 0; m_pos < m_arg.size(); ++m_pos )
-                visitChar( m_arg[m_pos] );
-            if( m_mode == Name )
-                addPattern<TestSpec::NamePattern>();
-            return *this;
+        template <typename... Gs>
+        Generators(Gs &&... moreGenerators) {
+            m_generators.reserve(sizeof...(Gs));
+            populate(std::forward<Gs>(moreGenerators)...);
         }
-        TestSpec testSpec() {
-            addFilter();
-            return m_testSpec;
+
+        T const& get() const override {
+            return m_generators[m_current].get();
         }
-    private:
-        void 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();
-            }
-            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 startNewMode( Mode mode, std::size_t start ) {
-            m_mode = mode;
-            m_start = start;
-        }
-        void escape() {
-            if( m_mode == None )
-                m_start = m_pos;
-            m_mode = EscapedName;
-            m_escapeChars.push_back( m_pos );
-        }
-        std::string subString() const { return m_arg.substr( m_start, m_pos - m_start ); }
-        template<typename T>
-        void addPattern() {
-            std::string token = subString();
-            for( 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() ) {
-                Ptr<TestSpec::Pattern> pattern = new T( token );
-                if( m_exclusion )
-                    pattern = new TestSpec::ExcludedPattern( pattern );
-                m_currentFilter.m_patterns.push_back( pattern );
+
+        bool next() override {
+            if (m_current >= m_generators.size()) {
+                return false;
             }
-            m_exclusion = false;
-            m_mode = None;
-        }
-        void addFilter() {
-            if( !m_currentFilter.m_patterns.empty() ) {
-                m_testSpec.m_filters.push_back( m_currentFilter );
-                m_currentFilter = TestSpec::Filter();
+            const bool current_status = m_generators[m_current].next();
+            if (!current_status) {
+                ++m_current;
             }
+            return m_current < m_generators.size();
         }
     };
-    inline TestSpec parseTestSpec( std::string const& arg ) {
-        return TestSpecParser( ITagAliasRegistry::get() ).parse( arg ).testSpec();
-    }
-
-} // namespace Catch
 
-#ifdef __clang__
-#pragma clang diagnostic pop
-#endif
+    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 );
+    }
 
-// #included from: catch_interfaces_config.h
-#define TWOBLUECUBES_CATCH_INTERFACES_CONFIG_H_INCLUDED
+    // Tag type to signal that a generator sequence should convert arguments to a specific type
+    template <typename T>
+    struct as {};
 
-#include <iosfwd>
-#include <string>
-#include <vector>
+    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>
+    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 )... );
+    }
 
-namespace Catch {
+    auto acquireGeneratorTracker( StringRef generatorName, SourceLineInfo const& lineInfo ) -> IGeneratorTracker&;
 
-    struct Verbosity { enum Level {
-        NoOutput = 0,
-        Quiet,
-        Normal
-    }; };
+    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;
 
-    struct WarnAbout { enum What {
-        Nothing = 0x00,
-        NoAssertions = 0x01
-    }; };
+        IGeneratorTracker& tracker = acquireGeneratorTracker( generatorName, lineInfo );
+        if (!tracker.hasGenerator()) {
+            tracker.setGenerator(pf::make_unique<Generators<UnderlyingType>>(generatorExpression()));
+        }
 
-    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
-    }; };
+        auto const& generator = static_cast<IGenerator<UnderlyingType> const&>( *tracker.getGenerator() );
+        return generator.get();
+    }
 
-    class TestSpec;
+} // namespace Generators
+} // namespace Catch
 
-    struct IConfig : IShared {
+#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
 
-        virtual ~IConfig();
+namespace Catch {
+namespace Generators {
 
-        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 int abortAfter() const = 0;
-        virtual bool showInvisibles() const = 0;
-        virtual ShowDurations::OrNot showDurations() const = 0;
-        virtual TestSpec const& testSpec() 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;
+    template <typename T>
+    class TakeGenerator : public IGenerator<T> {
+        GeneratorWrapper<T> m_generator;
+        size_t m_returned = 0;
+        size_t m_target;
+    public:
+        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;
+            }
 
+            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;
+        }
     };
-}
-
-// #included from: catch_stream.h
-#define TWOBLUECUBES_CATCH_STREAM_H_INCLUDED
 
-// #included from: catch_streambuf.h
-#define TWOBLUECUBES_CATCH_STREAMBUF_H_INCLUDED
-
-#include <streambuf>
-
-namespace Catch {
+    template <typename T>
+    GeneratorWrapper<T> take(size_t target, GeneratorWrapper<T>&& generator) {
+        return GeneratorWrapper<T>(pf::make_unique<TakeGenerator<T>>(target, std::move(generator)));
+    }
 
-    class StreamBufBase : public std::streambuf {
+    template <typename T, typename Predicate>
+    class FilterGenerator : public IGenerator<T> {
+        GeneratorWrapper<T> m_generator;
+        Predicate m_predicate;
     public:
-        virtual ~StreamBufBase() CATCH_NOEXCEPT;
-    };
-}
-
-#include <streambuf>
-#include <ostream>
-#include <fstream>
-#include <memory>
+        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"));
+                }
+            }
+        }
 
-namespace Catch {
+        T const& get() const override {
+            return m_generator.get();
+        }
 
-    std::ostream& cout();
-    std::ostream& cerr();
-    std::ostream& clog();
+        bool next() override {
+            return nextImpl();
+        }
 
-    struct IStream {
-        virtual ~IStream() CATCH_NOEXCEPT;
-        virtual std::ostream& stream() const = 0;
+    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;
+        }
     };
 
-    class FileStream : public IStream {
-        mutable std::ofstream m_ofs;
+    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>
+    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:
-        FileStream( std::string const& filename );
-        virtual ~FileStream() CATCH_NOEXCEPT;
-    public: // IStream
-        virtual std::ostream& stream() const CATCH_OVERRIDE;
-    };
+        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");
+        }
 
-    class CoutStream : public IStream {
-        mutable std::ostream m_os;
-    public:
-        CoutStream();
-        virtual ~CoutStream() CATCH_NOEXCEPT;
+        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];
+        }
 
-    public: // IStream
-        virtual std::ostream& stream() const CATCH_OVERRIDE;
-    };
+        bool next() override {
+            // There are 2 basic cases:
+            // 1) We are still reading the generator
+            // 2) We are reading our own cache
 
-    class DebugOutStream : public IStream {
-        CATCH_AUTO_PTR( StreamBufBase ) m_streamBuf;
-        mutable std::ostream m_os;
-    public:
-        DebugOutStream();
-        virtual ~DebugOutStream() CATCH_NOEXCEPT;
+            // 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;
+            }
 
-    public: // IStream
-        virtual std::ostream& stream() const CATCH_OVERRIDE;
+            // 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;
+        }
     };
-}
-
-#include <memory>
-#include <vector>
-#include <string>
-#include <stdexcept>
 
-#ifndef CATCH_CONFIG_CONSOLE_WIDTH
-#define CATCH_CONFIG_CONSOLE_WIDTH 80
-#endif
-
-namespace Catch {
-
-    struct ConfigData {
+    template <typename T>
+    GeneratorWrapper<T> repeat(size_t repeats, GeneratorWrapper<T>&& generator) {
+        return GeneratorWrapper<T>(pf::make_unique<RepeatGenerator<T>>(repeats, std::move(generator)));
+    }
 
-        ConfigData()
-        :   listTests( false ),
-            listTags( false ),
-            listReporters( false ),
-            listTestNamesOnly( false ),
-            listExtraInfo( false ),
-            showSuccessfulTests( false ),
-            shouldDebugBreak( false ),
-            noThrow( false ),
-            showHelp( false ),
-            showInvisibles( false ),
-            filenamesAsTags( false ),
-            libIdentify( false ),
-            abortAfter( -1 ),
-            rngSeed( 0 ),
-            verbosity( Verbosity::Normal ),
-            warnings( WarnAbout::Nothing ),
-            showDurations( ShowDurations::DefaultForReporter ),
-            runOrder( RunTests::InDeclarationOrder ),
-            useColour( UseColour::Auto ),
-            waitForKeypress( WaitForKeypress::Never )
+    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()))
         {}
 
-        bool listTests;
-        bool listTags;
-        bool listReporters;
-        bool listTestNamesOnly;
-        bool listExtraInfo;
-
-        bool showSuccessfulTests;
-        bool shouldDebugBreak;
-        bool noThrow;
-        bool showHelp;
-        bool showInvisibles;
-        bool filenamesAsTags;
-        bool libIdentify;
-
-        int abortAfter;
-        unsigned int rngSeed;
-
-        Verbosity::Level verbosity;
-        WarnAbout::What warnings;
-        ShowDurations::OrNot showDurations;
-        RunTests::InWhatOrder runOrder;
-        UseColour::YesOrNo useColour;
-        WaitForKeypress::When waitForKeypress;
-
-        std::string outputFilename;
-        std::string name;
-        std::string processName;
-
-        std::vector<std::string> reporterNames;
-        std::vector<std::string> testsOrTags;
-        std::vector<std::string> sectionsToRun;
+        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;
+        }
     };
 
-    class Config : public SharedImpl<IConfig> {
-    private:
-        Config( Config const& other );
-        Config& operator = ( Config const& other );
-        virtual void dummy();
-    public:
+    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))
+        );
+    }
 
-        Config()
-        {}
+    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))
+        );
+    }
 
-        Config( ConfigData const& data )
-        :   m_data( data ),
-            m_stream( openStream() )
+    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))
         {
-            if( !data.testsOrTags.empty() ) {
-                TestSpecParser parser( ITagAliasRegistry::get() );
-                for( std::size_t i = 0; i < data.testsOrTags.size(); ++i )
-                    parser.parse( data.testsOrTags[i] );
-                m_testSpec = parser.testSpec();
+            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());
+                }
             }
         }
-
-        virtual ~Config() {}
-
-        std::string const& getFilename() const {
-            return m_data.outputFilename ;
+        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;
         }
+    };
 
-        bool listTests() const { return m_data.listTests; }
-        bool listTestNamesOnly() const { return m_data.listTestNamesOnly; }
-        bool listTags() const { return m_data.listTags; }
-        bool listReporters() const { return m_data.listReporters; }
-        bool listExtraInfo() const { return m_data.listExtraInfo; }
+    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))
+        );
+    }
 
-        std::string getProcessName() const { return m_data.processName; }
+} // namespace Generators
+} // namespace Catch
 
-        std::vector<std::string> const& getReporterNames() const { return m_data.reporterNames; }
-        std::vector<std::string> const& getSectionsToRun() const CATCH_OVERRIDE { return m_data.sectionsToRun; }
+// end catch_generators_generic.hpp
+// start catch_generators_specific.hpp
 
-        virtual TestSpec const& testSpec() const CATCH_OVERRIDE { return m_testSpec; }
+// start catch_context.h
 
-        bool showHelp() const { return m_data.showHelp; }
+#include <memory>
 
-        // IConfig interface
-        virtual bool allowThrows() const CATCH_OVERRIDE                 { return !m_data.noThrow; }
-        virtual std::ostream& stream() const CATCH_OVERRIDE             { return m_stream->stream(); }
-        virtual std::string name() const CATCH_OVERRIDE                 { return m_data.name.empty() ? m_data.processName : m_data.name; }
-        virtual bool includeSuccessfulResults() const CATCH_OVERRIDE    { return m_data.showSuccessfulTests; }
-        virtual bool warnAboutMissingAssertions() const CATCH_OVERRIDE  { return m_data.warnings & WarnAbout::NoAssertions; }
-        virtual ShowDurations::OrNot showDurations() const CATCH_OVERRIDE { return m_data.showDurations; }
-        virtual RunTests::InWhatOrder runOrder() const CATCH_OVERRIDE   { return m_data.runOrder; }
-        virtual unsigned int rngSeed() const CATCH_OVERRIDE             { return m_data.rngSeed; }
-        virtual UseColour::YesOrNo useColour() const CATCH_OVERRIDE     { return m_data.useColour; }
-        virtual bool shouldDebugBreak() const CATCH_OVERRIDE { return m_data.shouldDebugBreak; }
-        virtual int abortAfter() const CATCH_OVERRIDE { return m_data.abortAfter; }
-        virtual bool showInvisibles() const CATCH_OVERRIDE { return m_data.showInvisibles; }
+namespace Catch {
 
-    private:
+    struct IResultCapture;
+    struct IRunner;
+    struct IConfig;
+    struct IMutableContext;
 
-        IStream const* openStream() {
-            if( m_data.outputFilename.empty() )
-                return new CoutStream();
-            else if( m_data.outputFilename[0] == '%' ) {
-                if( m_data.outputFilename == "%debug" )
-                    return new DebugOutStream();
-                else
-                    throw std::domain_error( "Unrecognised stream: " + m_data.outputFilename );
-            }
-            else
-                return new FileStream( m_data.outputFilename );
-        }
-        ConfigData m_data;
+    using IConfigPtr = std::shared_ptr<IConfig const>;
 
-        CATCH_AUTO_PTR( IStream const ) m_stream;
-        TestSpec m_testSpec;
+    struct IContext
+    {
+        virtual ~IContext();
+
+        virtual IResultCapture* getResultCapture() = 0;
+        virtual IRunner* getRunner() = 0;
+        virtual IConfigPtr const& getConfig() const = 0;
     };
 
-} // end namespace Catch
-
-// #included from: catch_clara.h
-#define TWOBLUECUBES_CATCH_CLARA_H_INCLUDED
-
-// Use Catch's value for console width (store Clara's off to the side, if present)
-#ifdef CLARA_CONFIG_CONSOLE_WIDTH
-#define CATCH_TEMP_CLARA_CONFIG_CONSOLE_WIDTH CLARA_CONFIG_CONSOLE_WIDTH
-#undef CLARA_CONFIG_CONSOLE_WIDTH
-#endif
-#define CLARA_CONFIG_CONSOLE_WIDTH CATCH_CONFIG_CONSOLE_WIDTH
-
-// Declare Clara inside the Catch namespace
-#define STITCH_CLARA_OPEN_NAMESPACE namespace Catch {
-// #included from: ../external/clara.h
-
-// Version 0.0.2.4
-
-// Only use header guard if we are not using an outer namespace
-#if !defined(TWOBLUECUBES_CLARA_H_INCLUDED) || defined(STITCH_CLARA_OPEN_NAMESPACE)
+    struct IMutableContext : IContext
+    {
+        virtual ~IMutableContext();
+        virtual void setResultCapture( IResultCapture* resultCapture ) = 0;
+        virtual void setRunner( IRunner* runner ) = 0;
+        virtual void setConfig( IConfigPtr const& config ) = 0;
 
-#ifndef STITCH_CLARA_OPEN_NAMESPACE
-#define TWOBLUECUBES_CLARA_H_INCLUDED
-#define STITCH_CLARA_OPEN_NAMESPACE
-#define STITCH_CLARA_CLOSE_NAMESPACE
-#else
-#define STITCH_CLARA_CLOSE_NAMESPACE }
-#endif
+    private:
+        static IMutableContext *currentContext;
+        friend IMutableContext& getCurrentMutableContext();
+        friend void cleanUpContext();
+        static void createContext();
+    };
 
-#define STITCH_TBC_TEXT_FORMAT_OPEN_NAMESPACE STITCH_CLARA_OPEN_NAMESPACE
+    inline IMutableContext& getCurrentMutableContext()
+    {
+        if( !IMutableContext::currentContext )
+            IMutableContext::createContext();
+        // NOLINTNEXTLINE(clang-analyzer-core.uninitialized.UndefReturn)
+        return *IMutableContext::currentContext;
+    }
 
-// ----------- #included from tbc_text_format.h -----------
+    inline IContext& getCurrentContext()
+    {
+        return getCurrentMutableContext();
+    }
 
-// Only use header guard if we are not using an outer namespace
-#if !defined(TBC_TEXT_FORMAT_H_INCLUDED) || defined(STITCH_TBC_TEXT_FORMAT_OUTER_NAMESPACE)
-#ifndef STITCH_TBC_TEXT_FORMAT_OUTER_NAMESPACE
-#define TBC_TEXT_FORMAT_H_INCLUDED
-#endif
+    void cleanUpContext();
 
-#include <string>
-#include <vector>
-#include <sstream>
-#include <algorithm>
-#include <cctype>
+    class SimplePcg32;
+    SimplePcg32& rng();
+}
 
-// Use optional outer namespace
-#ifdef STITCH_TBC_TEXT_FORMAT_OUTER_NAMESPACE
-namespace STITCH_TBC_TEXT_FORMAT_OUTER_NAMESPACE {
-#endif
+// end catch_context.h
+// start catch_interfaces_config.h
 
-namespace Tbc {
+// start catch_option.hpp
 
-#ifdef TBC_TEXT_FORMAT_CONSOLE_WIDTH
-    const unsigned int consoleWidth = TBC_TEXT_FORMAT_CONSOLE_WIDTH;
-#else
-    const unsigned int consoleWidth = 80;
-#endif
+namespace Catch {
 
-    struct TextAttributes {
-        TextAttributes()
-        :   initialIndent( std::string::npos ),
-            indent( 0 ),
-            width( consoleWidth-1 ),
-            tabChar( '\t' )
+    // 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 )
         {}
 
-        TextAttributes& setInitialIndent( std::size_t _value )  { initialIndent = _value; return *this; }
-        TextAttributes& setIndent( std::size_t _value )         { indent = _value; return *this; }
-        TextAttributes& setWidth( std::size_t _value )          { width = _value; return *this; }
-        TextAttributes& setTabChar( char _value )               { tabChar = _value; return *this; }
-
-        std::size_t initialIndent;  // indent of first line, or npos
-        std::size_t indent;         // indent of subsequent lines, or all if initialIndent is npos
-        std::size_t width;          // maximum width of text, including indent. Longer text will wrap
-        char tabChar;               // If this char is seen the indent is changed to current pos
-    };
-
-    class Text {
-    public:
-        Text( std::string const& _str, TextAttributes const& _attr = TextAttributes() )
-        : attr( _attr )
-        {
-            std::string wrappableChars = " [({.,/|\\-";
-            std::size_t indent = _attr.initialIndent != std::string::npos
-                ? _attr.initialIndent
-                : _attr.indent;
-            std::string remainder = _str;
-
-            while( !remainder.empty() ) {
-                if( lines.size() >= 1000 ) {
-                    lines.push_back( "... message truncated due to excessive size" );
-                    return;
-                }
-                std::size_t tabPos = std::string::npos;
-                std::size_t width = (std::min)( remainder.size(), _attr.width - indent );
-                std::size_t pos = remainder.find_first_of( '\n' );
-                if( pos <= width ) {
-                    width = pos;
-                }
-                pos = remainder.find_last_of( _attr.tabChar, width );
-                if( pos != std::string::npos ) {
-                    tabPos = pos;
-                    if( remainder[width] == '\n' )
-                        width--;
-                    remainder = remainder.substr( 0, tabPos ) + remainder.substr( tabPos+1 );
-                }
+        ~Option() {
+            reset();
+        }
 
-                if( width == remainder.size() ) {
-                    spliceLine( indent, remainder, width );
-                }
-                else if( remainder[width] == '\n' ) {
-                    spliceLine( indent, remainder, width );
-                    if( width <= 1 || remainder.size() != 1 )
-                        remainder = remainder.substr( 1 );
-                    indent = _attr.indent;
-                }
-                else {
-                    pos = remainder.find_last_of( wrappableChars, width );
-                    if( pos != std::string::npos && pos > 0 ) {
-                        spliceLine( indent, remainder, pos );
-                        if( remainder[0] == ' ' )
-                            remainder = remainder.substr( 1 );
-                    }
-                    else {
-                        spliceLine( indent, remainder, width-1 );
-                        lines.back() += "-";
-                    }
-                    if( lines.size() == 1 )
-                        indent = _attr.indent;
-                    if( tabPos != std::string::npos )
-                        indent += tabPos;
-                }
+        Option& operator= ( Option const& _other ) {
+            if( &_other != this ) {
+                reset();
+                if( _other )
+                    nullableValue = new( storage ) T( *_other );
             }
+            return *this;
+        }
+        Option& operator = ( T const& _value ) {
+            reset();
+            nullableValue = new( storage ) T( _value );
+            return *this;
         }
 
-        void spliceLine( std::size_t _indent, std::string& _remainder, std::size_t _pos ) {
-            lines.push_back( std::string( _indent, ' ' ) + _remainder.substr( 0, _pos ) );
-            _remainder = _remainder.substr( _pos );
+        void reset() {
+            if( nullableValue )
+                nullableValue->~T();
+            nullableValue = nullptr;
         }
 
-        typedef std::vector<std::string>::const_iterator const_iterator;
+        T& operator*() { return *nullableValue; }
+        T const& operator*() const { return *nullableValue; }
+        T* operator->() { return nullableValue; }
+        const T* operator->() const { return nullableValue; }
 
-        const_iterator begin() const { return lines.begin(); }
-        const_iterator end() const { return lines.end(); }
-        std::string const& last() const { return lines.back(); }
-        std::size_t size() const { return lines.size(); }
-        std::string const& operator[]( std::size_t _index ) const { return lines[_index]; }
-        std::string toString() const {
-            std::ostringstream oss;
-            oss << *this;
-            return oss.str();
+        T valueOr( T const& defaultValue ) const {
+            return nullableValue ? *nullableValue : defaultValue;
         }
 
-        friend std::ostream& operator << ( std::ostream& _stream, Text const& _text ) {
-            for( Text::const_iterator it = _text.begin(), itEnd = _text.end();
-                it != itEnd; ++it ) {
-                if( it != _text.begin() )
-                    _stream << "\n";
-                _stream << *it;
-            }
-            return _stream;
+        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:
-        std::string str;
-        TextAttributes attr;
-        std::vector<std::string> lines;
+        T *nullableValue;
+        alignas(alignof(T)) char storage[sizeof(T)];
     };
 
-} // end namespace Tbc
-
-#ifdef STITCH_TBC_TEXT_FORMAT_OUTER_NAMESPACE
-} // end outer namespace
-#endif
+} // end namespace Catch
 
-#endif // TBC_TEXT_FORMAT_H_INCLUDED
+// end catch_option.hpp
+#include <chrono>
+#include <iosfwd>
+#include <string>
+#include <vector>
+#include <memory>
 
-// ----------- end of #include from tbc_text_format.h -----------
-// ........... back in clara.h
+namespace Catch {
 
-#undef STITCH_TBC_TEXT_FORMAT_OPEN_NAMESPACE
+    enum class Verbosity {
+        Quiet = 0,
+        Normal,
+        High
+    };
 
-// ----------- #included from clara_compilers.h -----------
+    struct WarnAbout { enum What {
+        Nothing = 0x00,
+        NoAssertions = 0x01,
+        NoTests = 0x02
+    }; };
 
-#ifndef TWOBLUECUBES_CLARA_COMPILERS_H_INCLUDED
-#define TWOBLUECUBES_CLARA_COMPILERS_H_INCLUDED
+    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
+    }; };
 
-// Detect a number of compiler features - mostly C++11/14 conformance - by compiler
-// The following features are defined:
-//
-// CLARA_CONFIG_CPP11_NULLPTR : is nullptr supported?
-// CLARA_CONFIG_CPP11_NOEXCEPT : is noexcept supported?
-// CLARA_CONFIG_CPP11_GENERATED_METHODS : The delete and default keywords for compiler generated methods
-// CLARA_CONFIG_CPP11_OVERRIDE : is override supported?
-// CLARA_CONFIG_CPP11_UNIQUE_PTR : is unique_ptr supported (otherwise use auto_ptr)
+    class TestSpec;
 
-// CLARA_CONFIG_CPP11_OR_GREATER : Is C++11 supported?
+    struct IConfig : NonCopyable {
 
-// CLARA_CONFIG_VARIADIC_MACROS : are variadic macros supported?
+        virtual ~IConfig();
 
-// In general each macro has a _NO_<feature name> form
-// (e.g. CLARA_CONFIG_CPP11_NO_NULLPTR) which disables the feature.
-// Many features, at point of detection, define an _INTERNAL_ macro, so they
-// can be combined, en-mass, with the _NO_ forms later.
+        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;
 
-// All the C++11 features can be disabled with CLARA_CONFIG_NO_CPP11
+        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;
+    };
 
-#ifdef __clang__
+    using IConfigPtr = std::shared_ptr<IConfig const>;
+}
 
-#if __has_feature(cxx_nullptr)
-#define CLARA_INTERNAL_CONFIG_CPP11_NULLPTR
-#endif
+// end catch_interfaces_config.h
+// start catch_random_number_generator.h
 
-#if __has_feature(cxx_noexcept)
-#define CLARA_INTERNAL_CONFIG_CPP11_NOEXCEPT
-#endif
+#include <cstdint>
 
-#endif // __clang__
+namespace Catch {
 
-////////////////////////////////////////////////////////////////////////////////
-// GCC
-#ifdef __GNUC__
+    // 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);
+        }
 
-#if __GNUC__ == 4 && __GNUC_MINOR__ >= 6 && defined(__GXX_EXPERIMENTAL_CXX0X__)
-#define CLARA_INTERNAL_CONFIG_CPP11_NULLPTR
-#endif
+        // Provide some default initial state for the default constructor
+        SimplePcg32():SimplePcg32(0xed743cc4U) {}
 
-// - otherwise more recent versions define __cplusplus >= 201103L
-// and will get picked up below
+        explicit SimplePcg32(result_type seed_);
 
-#endif // __GNUC__
+        void seed(result_type seed_);
+        void discard(uint64_t skip);
 
-////////////////////////////////////////////////////////////////////////////////
-// Visual C++
-#ifdef _MSC_VER
+        result_type operator()();
 
-#if (_MSC_VER >= 1600)
-#define CLARA_INTERNAL_CONFIG_CPP11_NULLPTR
-#define CLARA_INTERNAL_CONFIG_CPP11_UNIQUE_PTR
-#endif
+    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;
+    };
 
-#if (_MSC_VER >= 1900 ) // (VC++ 13 (VS2015))
-#define CLARA_INTERNAL_CONFIG_CPP11_NOEXCEPT
-#define CLARA_INTERNAL_CONFIG_CPP11_GENERATED_METHODS
-#endif
+} // end namespace Catch
 
-#endif // _MSC_VER
+// end catch_random_number_generator.h
+#include <random>
 
-////////////////////////////////////////////////////////////////////////////////
-// C++ language feature support
+namespace Catch {
+namespace Generators {
 
-// catch all support for C++11
-#if defined(__cplusplus) && __cplusplus >= 201103L
+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:
 
-#define CLARA_CPP11_OR_GREATER
+    RandomFloatingGenerator(Float a, Float b):
+        m_rng(rng()),
+        m_dist(a, b) {
+        static_cast<void>(next());
+    }
 
-#if !defined(CLARA_INTERNAL_CONFIG_CPP11_NULLPTR)
-#define CLARA_INTERNAL_CONFIG_CPP11_NULLPTR
-#endif
+    Float const& get() const override {
+        return m_current_number;
+    }
+    bool next() override {
+        m_current_number = m_dist(m_rng);
+        return true;
+    }
+};
 
-#ifndef CLARA_INTERNAL_CONFIG_CPP11_NOEXCEPT
-#define CLARA_INTERNAL_CONFIG_CPP11_NOEXCEPT
-#endif
+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:
 
-#ifndef CLARA_INTERNAL_CONFIG_CPP11_GENERATED_METHODS
-#define CLARA_INTERNAL_CONFIG_CPP11_GENERATED_METHODS
-#endif
+    RandomIntegerGenerator(Integer a, Integer b):
+        m_rng(rng()),
+        m_dist(a, b) {
+        static_cast<void>(next());
+    }
 
-#if !defined(CLARA_INTERNAL_CONFIG_CPP11_OVERRIDE)
-#define CLARA_INTERNAL_CONFIG_CPP11_OVERRIDE
-#endif
-#if !defined(CLARA_INTERNAL_CONFIG_CPP11_UNIQUE_PTR)
-#define CLARA_INTERNAL_CONFIG_CPP11_UNIQUE_PTR
-#endif
+    Integer const& get() const override {
+        return m_current_number;
+    }
+    bool next() override {
+        m_current_number = m_dist(m_rng);
+        return true;
+    }
+};
 
-#endif // __cplusplus >= 201103L
+// 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)
+    );
+}
 
-// Now set the actual defines based on the above + anything the user has configured
-#if defined(CLARA_INTERNAL_CONFIG_CPP11_NULLPTR) && !defined(CLARA_CONFIG_CPP11_NO_NULLPTR) && !defined(CLARA_CONFIG_CPP11_NULLPTR) && !defined(CLARA_CONFIG_NO_CPP11)
-#define CLARA_CONFIG_CPP11_NULLPTR
-#endif
-#if defined(CLARA_INTERNAL_CONFIG_CPP11_NOEXCEPT) && !defined(CLARA_CONFIG_CPP11_NO_NOEXCEPT) && !defined(CLARA_CONFIG_CPP11_NOEXCEPT) && !defined(CLARA_CONFIG_NO_CPP11)
-#define CLARA_CONFIG_CPP11_NOEXCEPT
-#endif
-#if defined(CLARA_INTERNAL_CONFIG_CPP11_GENERATED_METHODS) && !defined(CLARA_CONFIG_CPP11_NO_GENERATED_METHODS) && !defined(CLARA_CONFIG_CPP11_GENERATED_METHODS) && !defined(CLARA_CONFIG_NO_CPP11)
-#define CLARA_CONFIG_CPP11_GENERATED_METHODS
-#endif
-#if defined(CLARA_INTERNAL_CONFIG_CPP11_OVERRIDE) && !defined(CLARA_CONFIG_NO_OVERRIDE) && !defined(CLARA_CONFIG_CPP11_OVERRIDE) && !defined(CLARA_CONFIG_NO_CPP11)
-#define CLARA_CONFIG_CPP11_OVERRIDE
-#endif
-#if defined(CLARA_INTERNAL_CONFIG_CPP11_UNIQUE_PTR) && !defined(CLARA_CONFIG_NO_UNIQUE_PTR) && !defined(CLARA_CONFIG_CPP11_UNIQUE_PTR) && !defined(CLARA_CONFIG_NO_CPP11)
-#define CLARA_CONFIG_CPP11_UNIQUE_PTR
-#endif
+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)
+    );
+}
 
-// noexcept support:
-#if defined(CLARA_CONFIG_CPP11_NOEXCEPT) && !defined(CLARA_NOEXCEPT)
-#define CLARA_NOEXCEPT noexcept
-#  define CLARA_NOEXCEPT_IS(x) noexcept(x)
-#else
-#define CLARA_NOEXCEPT throw()
-#  define CLARA_NOEXCEPT_IS(x)
-#endif
+template <typename T>
+class RangeGenerator final : public IGenerator<T> {
+    T m_current;
+    T m_end;
+    T m_step;
+    bool m_positive;
 
-// nullptr support
-#ifdef CLARA_CONFIG_CPP11_NULLPTR
-#define CLARA_NULL nullptr
-#else
-#define CLARA_NULL NULL
-#endif
+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");
+    }
 
-// override support
-#ifdef CLARA_CONFIG_CPP11_OVERRIDE
-#define CLARA_OVERRIDE override
-#else
-#define CLARA_OVERRIDE
-#endif
+    RangeGenerator(T const& start, T const& end):
+        RangeGenerator(start, end, (start < end) ? T(1) : T(-1))
+    {}
 
-// unique_ptr support
-#ifdef CLARA_CONFIG_CPP11_UNIQUE_PTR
-#   define CLARA_AUTO_PTR( T ) std::unique_ptr<T>
-#else
-#   define CLARA_AUTO_PTR( T ) std::auto_ptr<T>
-#endif
+    T const& get() const override {
+        return m_current;
+    }
 
-#endif // TWOBLUECUBES_CLARA_COMPILERS_H_INCLUDED
+    bool next() override {
+        m_current += m_step;
+        return (m_positive) ? (m_current < m_end) : (m_current > m_end);
+    }
+};
 
-// ----------- end of #include from clara_compilers.h -----------
-// ........... back in clara.h
+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));
+}
 
-#include <map>
-#include <stdexcept>
-#include <memory>
+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));
+}
 
-#if defined(WIN32) || defined(__WIN32__) || defined(_WIN32) || defined(_MSC_VER)
-#define CLARA_PLATFORM_WINDOWS
-#endif
+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");
 
-// Use optional outer namespace
-#ifdef STITCH_CLARA_OPEN_NAMESPACE
-STITCH_CLARA_OPEN_NAMESPACE
-#endif
+    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"));
+        }
+    }
 
-namespace Clara {
+    T const& get() const override {
+        return m_elems[m_current];
+    }
 
-    struct UnpositionalTag {};
+    bool next() override {
+        ++m_current;
+        return m_current != m_elems.size();
+    }
+};
 
-    extern UnpositionalTag _;
+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));
+}
 
-#ifdef CLARA_CONFIG_MAIN
-    UnpositionalTag _;
-#endif
+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 Detail {
+} // namespace Generators
+} // namespace Catch
 
-#ifdef CLARA_CONSOLE_WIDTH
-    const unsigned int consoleWidth = CLARA_CONFIG_CONSOLE_WIDTH;
-#else
-    const unsigned int consoleWidth = 80;
-#endif
+// end catch_generators_specific.hpp
 
-        using namespace Tbc;
+// These files are included here so the single_include script doesn't put them
+// in the conditionally compiled sections
+// start catch_test_case_info.h
 
-        inline bool startsWith( std::string const& str, std::string const& prefix ) {
-            return str.size() >= prefix.size() && str.substr( 0, prefix.size() ) == prefix;
-        }
+#include <string>
+#include <vector>
+#include <memory>
 
-        template<typename T> struct RemoveConstRef{ typedef T type; };
-        template<typename T> struct RemoveConstRef<T&>{ typedef T type; };
-        template<typename T> struct RemoveConstRef<T const&>{ typedef T type; };
-        template<typename T> struct RemoveConstRef<T const>{ typedef T type; };
+#ifdef __clang__
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wpadded"
+#endif
 
-        template<typename T>    struct IsBool       { static const bool value = false; };
-        template<>              struct IsBool<bool> { static const bool value = true; };
+namespace Catch {
 
-        template<typename T>
-        void convertInto( std::string const& _source, T& _dest ) {
-            std::stringstream ss;
-            ss << _source;
-            ss >> _dest;
-            if( ss.fail() )
-                throw std::runtime_error( "Unable to convert " + _source + " to destination type" );
-        }
-        inline void convertInto( std::string const& _source, std::string& _dest ) {
-            _dest = _source;
-        }
-        char toLowerCh(char c) {
-            return static_cast<char>( std::tolower( c ) );
-        }
-        inline void convertInto( std::string const& _source, bool& _dest ) {
-            std::string sourceLC = _source;
-            std::transform( sourceLC.begin(), sourceLC.end(), sourceLC.begin(), toLowerCh );
-            if( sourceLC == "y" || sourceLC == "1" || sourceLC == "true" || sourceLC == "yes" || sourceLC == "on" )
-                _dest = true;
-            else if( sourceLC == "n" || sourceLC == "0" || sourceLC == "false" || sourceLC == "no" || sourceLC == "off" )
-                _dest = false;
-            else
-                throw std::runtime_error( "Expected a boolean value but did not recognise:\n  '" + _source + "'" );
-        }
+    struct ITestInvoker;
 
-        template<typename ConfigT>
-        struct IArgFunction {
-            virtual ~IArgFunction() {}
-#ifdef CLARA_CONFIG_CPP11_GENERATED_METHODS
-            IArgFunction()                      = default;
-            IArgFunction( IArgFunction const& ) = default;
-#endif
-            virtual void set( ConfigT& config, std::string const& value ) const = 0;
-            virtual bool takesArg() const = 0;
-            virtual IArgFunction* clone() const = 0;
+    struct TestCaseInfo {
+        enum SpecialProperties{
+            None = 0,
+            IsHidden = 1 << 1,
+            ShouldFail = 1 << 2,
+            MayFail = 1 << 3,
+            Throws = 1 << 4,
+            NonPortable = 1 << 5,
+            Benchmark = 1 << 6
         };
 
-        template<typename ConfigT>
-        class BoundArgFunction {
-        public:
-            BoundArgFunction() : functionObj( CLARA_NULL ) {}
-            BoundArgFunction( IArgFunction<ConfigT>* _functionObj ) : functionObj( _functionObj ) {}
-            BoundArgFunction( BoundArgFunction const& other ) : functionObj( other.functionObj ? other.functionObj->clone() : CLARA_NULL ) {}
-            BoundArgFunction& operator = ( BoundArgFunction const& other ) {
-                IArgFunction<ConfigT>* newFunctionObj = other.functionObj ? other.functionObj->clone() : CLARA_NULL;
-                delete functionObj;
-                functionObj = newFunctionObj;
-                return *this;
-            }
-            ~BoundArgFunction() { delete functionObj; }
+        TestCaseInfo(   std::string const& _name,
+                        std::string const& _className,
+                        std::string const& _description,
+                        std::vector<std::string> const& _tags,
+                        SourceLineInfo const& _lineInfo );
 
-            void set( ConfigT& config, std::string const& value ) const {
-                functionObj->set( config, value );
-            }
-            bool takesArg() const { return functionObj->takesArg(); }
+        friend void setTags( TestCaseInfo& testCaseInfo, std::vector<std::string> tags );
 
-            bool isSet() const {
-                return functionObj != CLARA_NULL;
-            }
-        private:
-            IArgFunction<ConfigT>* functionObj;
-        };
-
-        template<typename C>
-        struct NullBinder : IArgFunction<C>{
-            virtual void set( C&, std::string const& ) const {}
-            virtual bool takesArg() const { return true; }
-            virtual IArgFunction<C>* clone() const { return new NullBinder( *this ); }
-        };
-
-        template<typename C, typename M>
-        struct BoundDataMember : IArgFunction<C>{
-            BoundDataMember( M C::* _member ) : member( _member ) {}
-            virtual void set( C& p, std::string const& stringValue ) const {
-                convertInto( stringValue, p.*member );
-            }
-            virtual bool takesArg() const { return !IsBool<M>::value; }
-            virtual IArgFunction<C>* clone() const { return new BoundDataMember( *this ); }
-            M C::* member;
-        };
-        template<typename C, typename M>
-        struct BoundUnaryMethod : IArgFunction<C>{
-            BoundUnaryMethod( void (C::*_member)( M ) ) : member( _member ) {}
-            virtual void set( C& p, std::string const& stringValue ) const {
-                typename RemoveConstRef<M>::type value;
-                convertInto( stringValue, value );
-                (p.*member)( value );
-            }
-            virtual bool takesArg() const { return !IsBool<M>::value; }
-            virtual IArgFunction<C>* clone() const { return new BoundUnaryMethod( *this ); }
-            void (C::*member)( M );
-        };
-        template<typename C>
-        struct BoundNullaryMethod : IArgFunction<C>{
-            BoundNullaryMethod( void (C::*_member)() ) : member( _member ) {}
-            virtual void set( C& p, std::string const& stringValue ) const {
-                bool value;
-                convertInto( stringValue, value );
-                if( value )
-                    (p.*member)();
-            }
-            virtual bool takesArg() const { return false; }
-            virtual IArgFunction<C>* clone() const { return new BoundNullaryMethod( *this ); }
-            void (C::*member)();
-        };
+        bool isHidden() const;
+        bool throws() const;
+        bool okToFail() const;
+        bool expectedToFail() const;
 
-        template<typename C>
-        struct BoundUnaryFunction : IArgFunction<C>{
-            BoundUnaryFunction( void (*_function)( C& ) ) : function( _function ) {}
-            virtual void set( C& obj, std::string const& stringValue ) const {
-                bool value;
-                convertInto( stringValue, value );
-                if( value )
-                    function( obj );
-            }
-            virtual bool takesArg() const { return false; }
-            virtual IArgFunction<C>* clone() const { return new BoundUnaryFunction( *this ); }
-            void (*function)( C& );
-        };
+        std::string tagsAsString() const;
 
-        template<typename C, typename T>
-        struct BoundBinaryFunction : IArgFunction<C>{
-            BoundBinaryFunction( void (*_function)( C&, T ) ) : function( _function ) {}
-            virtual void set( C& obj, std::string const& stringValue ) const {
-                typename RemoveConstRef<T>::type value;
-                convertInto( stringValue, value );
-                function( obj, value );
-            }
-            virtual bool takesArg() const { return !IsBool<T>::value; }
-            virtual IArgFunction<C>* clone() const { return new BoundBinaryFunction( *this ); }
-            void (*function)( C&, T );
-        };
+        std::string name;
+        std::string className;
+        std::string description;
+        std::vector<std::string> tags;
+        std::vector<std::string> lcaseTags;
+        SourceLineInfo lineInfo;
+        SpecialProperties properties;
+    };
 
-    } // namespace Detail
+    class TestCase : public TestCaseInfo {
+    public:
 
-    inline std::vector<std::string> argsToVector( int argc, char const* const* const argv ) {
-        std::vector<std::string> args( static_cast<std::size_t>( argc ) );
-        for( std::size_t i = 0; i < static_cast<std::size_t>( argc ); ++i )
-            args[i] = argv[i];
+        TestCase( ITestInvoker* testCase, TestCaseInfo&& info );
 
-        return args;
-    }
+        TestCase withName( std::string const& _newName ) const;
 
-    class Parser {
-        enum Mode { None, MaybeShortOpt, SlashOpt, ShortOpt, LongOpt, Positional };
-        Mode mode;
-        std::size_t from;
-        bool inQuotes;
-    public:
+        void invoke() const;
 
-        struct Token {
-            enum Type { Positional, ShortOpt, LongOpt };
-            Token( Type _type, std::string const& _data ) : type( _type ), data( _data ) {}
-            Type type;
-            std::string data;
-        };
+        TestCaseInfo const& getTestCaseInfo() const;
 
-        Parser() : mode( None ), from( 0 ), inQuotes( false ){}
+        bool operator == ( TestCase const& other ) const;
+        bool operator < ( TestCase const& other ) const;
 
-        void parseIntoTokens( std::vector<std::string> const& args, std::vector<Token>& tokens ) {
-            const std::string doubleDash = "--";
-            for( std::size_t i = 1; i < args.size() && args[i] != doubleDash; ++i )
-                parseIntoTokens( args[i], tokens);
-        }
+    private:
+        std::shared_ptr<ITestInvoker> test;
+    };
 
-        void parseIntoTokens( std::string const& arg, std::vector<Token>& tokens ) {
-            for( std::size_t i = 0; i < arg.size(); ++i ) {
-                char c = arg[i];
-                if( c == '"' )
-                    inQuotes = !inQuotes;
-                mode = handleMode( i, c, arg, tokens );
-            }
-            mode = handleMode( arg.size(), '\0', arg, tokens );
-        }
-        Mode handleMode( std::size_t i, char c, std::string const& arg, std::vector<Token>& tokens ) {
-            switch( mode ) {
-                case None: return handleNone( i, c );
-                case MaybeShortOpt: return handleMaybeShortOpt( i, c );
-                case ShortOpt:
-                case LongOpt:
-                case SlashOpt: return handleOpt( i, c, arg, tokens );
-                case Positional: return handlePositional( i, c, arg, tokens );
-                default: throw std::logic_error( "Unknown mode" );
-            }
-        }
+    TestCase makeTestCase(  ITestInvoker* testCase,
+                            std::string const& className,
+                            NameAndTags const& nameAndTags,
+                            SourceLineInfo const& lineInfo );
+}
 
-        Mode handleNone( std::size_t i, char c ) {
-            if( inQuotes ) {
-                from = i;
-                return Positional;
-            }
-            switch( c ) {
-                case '-': return MaybeShortOpt;
-#ifdef CLARA_PLATFORM_WINDOWS
-                case '/': from = i+1; return SlashOpt;
+#ifdef __clang__
+#pragma clang diagnostic pop
 #endif
-                default: from = i; return Positional;
-            }
-        }
-        Mode handleMaybeShortOpt( std::size_t i, char c ) {
-            switch( c ) {
-                case '-': from = i+1; return LongOpt;
-                default: from = i; return ShortOpt;
-            }
-        }
 
-        Mode handleOpt( std::size_t i, char c, std::string const& arg, std::vector<Token>& tokens ) {
-            if( std::string( ":=\0", 3 ).find( c ) == std::string::npos )
-                return mode;
+// end catch_test_case_info.h
+// start catch_interfaces_runner.h
 
-            std::string optName = arg.substr( from, i-from );
-            if( mode == ShortOpt )
-                for( std::size_t j = 0; j < optName.size(); ++j )
-                    tokens.push_back( Token( Token::ShortOpt, optName.substr( j, 1 ) ) );
-            else if( mode == SlashOpt && optName.size() == 1 )
-                tokens.push_back( Token( Token::ShortOpt, optName ) );
-            else
-                tokens.push_back( Token( Token::LongOpt, optName ) );
-            return None;
-        }
-        Mode handlePositional( std::size_t i, char c, std::string const& arg, std::vector<Token>& tokens ) {
-            if( inQuotes || std::string( "\0", 1 ).find( c ) == std::string::npos )
-                return mode;
+namespace Catch {
 
-            std::string data = arg.substr( from, i-from );
-            tokens.push_back( Token( Token::Positional, data ) );
-            return None;
-        }
+    struct IRunner {
+        virtual ~IRunner();
+        virtual bool aborting() const = 0;
     };
+}
 
-    template<typename ConfigT>
-    struct CommonArgProperties {
-        CommonArgProperties() {}
-        CommonArgProperties( Detail::BoundArgFunction<ConfigT> const& _boundField ) : boundField( _boundField ) {}
+// end catch_interfaces_runner.h
 
-        Detail::BoundArgFunction<ConfigT> boundField;
-        std::string description;
-        std::string detail;
-        std::string placeholder; // Only value if boundField takes an arg
+#ifdef __OBJC__
+// start catch_objc.hpp
 
-        bool takesArg() const {
-            return !placeholder.empty();
-        }
-        void validate() const {
-            if( !boundField.isSet() )
-                throw std::logic_error( "option not bound" );
-        }
-    };
-    struct OptionArgProperties {
-        std::vector<std::string> shortNames;
-        std::string longName;
+#import <objc/runtime.h>
 
-        bool hasShortName( std::string const& shortName ) const {
-            return std::find( shortNames.begin(), shortNames.end(), shortName ) != shortNames.end();
-        }
-        bool hasLongName( std::string const& _longName ) const {
-            return _longName == longName;
-        }
-    };
-    struct PositionalArgProperties {
-        PositionalArgProperties() : position( -1 ) {}
-        int position; // -1 means non-positional (floating)
+#include <string>
 
-        bool isFixedPositional() const {
-            return position != -1;
-        }
-    };
+// NB. Any general catch headers included here must be included
+// in catch.hpp first to make sure they are included by the single
+// header for non obj-usage
 
-    template<typename ConfigT>
-    class CommandLine {
+///////////////////////////////////////////////////////////////////////////////
+// This protocol is really only here for (self) documenting purposes, since
+// all its methods are optional.
+@protocol OcFixture
 
-        struct Arg : CommonArgProperties<ConfigT>, OptionArgProperties, PositionalArgProperties {
-            Arg() {}
-            Arg( Detail::BoundArgFunction<ConfigT> const& _boundField ) : CommonArgProperties<ConfigT>( _boundField ) {}
+@optional
 
-            using CommonArgProperties<ConfigT>::placeholder; // !TBD
+-(void) setUp;
+-(void) tearDown;
 
-            std::string dbgName() const {
-                if( !longName.empty() )
-                    return "--" + longName;
-                if( !shortNames.empty() )
-                    return "-" + shortNames[0];
-                return "positional args";
-            }
-            std::string commands() const {
-                std::ostringstream oss;
-                bool first = true;
-                std::vector<std::string>::const_iterator it = shortNames.begin(), itEnd = shortNames.end();
-                for(; it != itEnd; ++it ) {
-                    if( first )
-                        first = false;
-                    else
-                        oss << ", ";
-                    oss << "-" << *it;
-                }
-                if( !longName.empty() ) {
-                    if( !first )
-                        oss << ", ";
-                    oss << "--" << longName;
-                }
-                if( !placeholder.empty() )
-                    oss << " <" << placeholder << ">";
-                return oss.str();
-            }
-        };
+@end
 
-        typedef CLARA_AUTO_PTR( Arg ) ArgAutoPtr;
+namespace Catch {
 
-        friend void addOptName( Arg& arg, std::string const& optName )
-        {
-            if( optName.empty() )
-                return;
-            if( Detail::startsWith( optName, "--" ) ) {
-                if( !arg.longName.empty() )
-                    throw std::logic_error( "Only one long opt may be specified. '"
-                        + arg.longName
-                        + "' already specified, now attempting to add '"
-                        + optName + "'" );
-                arg.longName = optName.substr( 2 );
-            }
-            else if( Detail::startsWith( optName, "-" ) )
-                arg.shortNames.push_back( optName.substr( 1 ) );
-            else
-                throw std::logic_error( "option must begin with - or --. Option was: '" + optName + "'" );
-        }
-        friend void setPositionalArg( Arg& arg, int position )
-        {
-            arg.position = position;
-        }
+    class OcMethod : public ITestInvoker {
 
-        class ArgBuilder {
-        public:
-            ArgBuilder( Arg* arg ) : m_arg( arg ) {}
+    public:
+        OcMethod( Class cls, SEL sel ) : m_cls( cls ), m_sel( sel ) {}
 
-            // Bind a non-boolean data member (requires placeholder string)
-            template<typename C, typename M>
-            void bind( M C::* field, std::string const& placeholder ) {
-                m_arg->boundField = new Detail::BoundDataMember<C,M>( field );
-                m_arg->placeholder = placeholder;
-            }
-            // Bind a boolean data member (no placeholder required)
-            template<typename C>
-            void bind( bool C::* field ) {
-                m_arg->boundField = new Detail::BoundDataMember<C,bool>( field );
-            }
+        virtual void invoke() const {
+            id obj = [[m_cls alloc] init];
 
-            // Bind a method taking a single, non-boolean argument (requires a placeholder string)
-            template<typename C, typename M>
-            void bind( void (C::* unaryMethod)( M ), std::string const& placeholder ) {
-                m_arg->boundField = new Detail::BoundUnaryMethod<C,M>( unaryMethod );
-                m_arg->placeholder = placeholder;
-            }
+            performOptionalSelector( obj, @selector(setUp)  );
+            performOptionalSelector( obj, m_sel );
+            performOptionalSelector( obj, @selector(tearDown)  );
 
-            // Bind a method taking a single, boolean argument (no placeholder string required)
-            template<typename C>
-            void bind( void (C::* unaryMethod)( bool ) ) {
-                m_arg->boundField = new Detail::BoundUnaryMethod<C,bool>( unaryMethod );
-            }
+            arcSafeRelease( obj );
+        }
+    private:
+        virtual ~OcMethod() {}
 
-            // Bind a method that takes no arguments (will be called if opt is present)
-            template<typename C>
-            void bind( void (C::* nullaryMethod)() ) {
-                m_arg->boundField = new Detail::BoundNullaryMethod<C>( nullaryMethod );
-            }
+        Class m_cls;
+        SEL m_sel;
+    };
 
-            // Bind a free function taking a single argument - the object to operate on (no placeholder string required)
-            template<typename C>
-            void bind( void (* unaryFunction)( C& ) ) {
-                m_arg->boundField = new Detail::BoundUnaryFunction<C>( unaryFunction );
-            }
+    namespace Detail{
 
-            // Bind a free function taking a single argument - the object to operate on (requires a placeholder string)
-            template<typename C, typename T>
-            void bind( void (* binaryFunction)( C&, T ), std::string const& placeholder ) {
-                m_arg->boundField = new Detail::BoundBinaryFunction<C, T>( binaryFunction );
-                m_arg->placeholder = placeholder;
-            }
+        inline std::string getAnnotation(   Class cls,
+                                            std::string const& annotationName,
+                                            std::string const& testCaseName ) {
+            NSString* selStr = [[NSString alloc] initWithFormat:@"Catch_%s_%s", annotationName.c_str(), testCaseName.c_str()];
+            SEL sel = NSSelectorFromString( selStr );
+            arcSafeRelease( selStr );
+            id value = performOptionalSelector( cls, sel );
+            if( value )
+                return [(NSString*)value UTF8String];
+            return "";
+        }
+    }
 
-            ArgBuilder& describe( std::string const& description ) {
-                m_arg->description = description;
-                return *this;
-            }
-            ArgBuilder& detail( std::string const& detail ) {
-                m_arg->detail = detail;
-                return *this;
-            }
+    inline std::size_t registerTestMethods() {
+        std::size_t noTestMethods = 0;
+        int noClasses = objc_getClassList( nullptr, 0 );
 
-        protected:
-            Arg* m_arg;
-        };
+        Class* classes = (CATCH_UNSAFE_UNRETAINED Class *)malloc( sizeof(Class) * noClasses);
+        objc_getClassList( classes, noClasses );
 
-        class OptBuilder : public ArgBuilder {
-        public:
-            OptBuilder( Arg* arg ) : ArgBuilder( arg ) {}
-            OptBuilder( OptBuilder& other ) : ArgBuilder( other ) {}
+        for( int c = 0; c < noClasses; c++ ) {
+            Class cls = classes[c];
+            {
+                u_int count;
+                Method* methods = class_copyMethodList( cls, &count );
+                for( u_int m = 0; m < count ; m++ ) {
+                    SEL selector = method_getName(methods[m]);
+                    std::string methodName = sel_getName(selector);
+                    if( startsWith( methodName, "Catch_TestCase_" ) ) {
+                        std::string testCaseName = methodName.substr( 15 );
+                        std::string name = Detail::getAnnotation( cls, "Name", testCaseName );
+                        std::string desc = Detail::getAnnotation( cls, "Description", testCaseName );
+                        const char* className = class_getName( cls );
 
-            OptBuilder& operator[]( std::string const& optName ) {
-                addOptName( *ArgBuilder::m_arg, optName );
-                return *this;
+                        getMutableRegistryHub().registerTest( makeTestCase( new OcMethod( cls, selector ), className, NameAndTags( name.c_str(), desc.c_str() ), SourceLineInfo("",0) ) );
+                        noTestMethods++;
+                    }
+                }
+                free(methods);
             }
-        };
+        }
+        return noTestMethods;
+    }
 
-    public:
+#if !defined(CATCH_CONFIG_DISABLE_MATCHERS)
 
-        CommandLine()
-        :   m_boundProcessName( new Detail::NullBinder<ConfigT>() ),
-            m_highestSpecifiedArgPosition( 0 ),
-            m_throwOnUnrecognisedTokens( false )
-        {}
-        CommandLine( CommandLine const& other )
-        :   m_boundProcessName( other.m_boundProcessName ),
-            m_options ( other.m_options ),
-            m_positionalArgs( other.m_positionalArgs ),
-            m_highestSpecifiedArgPosition( other.m_highestSpecifiedArgPosition ),
-            m_throwOnUnrecognisedTokens( other.m_throwOnUnrecognisedTokens )
-        {
-            if( other.m_floatingArg.get() )
-                m_floatingArg.reset( new Arg( *other.m_floatingArg ) );
-        }
+    namespace Matchers {
+        namespace Impl {
+        namespace NSStringMatchers {
 
-        CommandLine& setThrowOnUnrecognisedTokens( bool shouldThrow = true ) {
-            m_throwOnUnrecognisedTokens = shouldThrow;
-            return *this;
-        }
+            struct StringHolder : MatcherBase<NSString*>{
+                StringHolder( NSString* substr ) : m_substr( [substr copy] ){}
+                StringHolder( StringHolder const& other ) : m_substr( [other.m_substr copy] ){}
+                StringHolder() {
+                    arcSafeRelease( m_substr );
+                }
 
-        OptBuilder operator[]( std::string const& optName ) {
-            m_options.push_back( Arg() );
-            addOptName( m_options.back(), optName );
-            OptBuilder builder( &m_options.back() );
-            return builder;
-        }
+                bool match( NSString* str ) const override {
+                    return false;
+                }
 
-        ArgBuilder operator[]( int position ) {
-            m_positionalArgs.insert( std::make_pair( position, Arg() ) );
-            if( position > m_highestSpecifiedArgPosition )
-                m_highestSpecifiedArgPosition = position;
-            setPositionalArg( m_positionalArgs[position], position );
-            ArgBuilder builder( &m_positionalArgs[position] );
-            return builder;
-        }
+                NSString* CATCH_ARC_STRONG m_substr;
+            };
 
-        // Invoke this with the _ instance
-        ArgBuilder operator[]( UnpositionalTag ) {
-            if( m_floatingArg.get() )
-                throw std::logic_error( "Only one unpositional argument can be added" );
-            m_floatingArg.reset( new Arg() );
-            ArgBuilder builder( m_floatingArg.get() );
-            return builder;
-        }
+            struct Equals : StringHolder {
+                Equals( NSString* substr ) : StringHolder( substr ){}
 
-        template<typename C, typename M>
-        void bindProcessName( M C::* field ) {
-            m_boundProcessName = new Detail::BoundDataMember<C,M>( field );
-        }
-        template<typename C, typename M>
-        void bindProcessName( void (C::*_unaryMethod)( M ) ) {
-            m_boundProcessName = new Detail::BoundUnaryMethod<C,M>( _unaryMethod );
-        }
+                bool match( NSString* str ) const override {
+                    return  (str != nil || m_substr == nil ) &&
+                            [str isEqualToString:m_substr];
+                }
 
-        void optUsage( std::ostream& os, std::size_t indent = 0, std::size_t width = Detail::consoleWidth ) const {
-            typename std::vector<Arg>::const_iterator itBegin = m_options.begin(), itEnd = m_options.end(), it;
-            std::size_t maxWidth = 0;
-            for( it = itBegin; it != itEnd; ++it )
-                maxWidth = (std::max)( maxWidth, it->commands().size() );
+                std::string describe() const override {
+                    return "equals string: " + Catch::Detail::stringify( m_substr );
+                }
+            };
 
-            for( it = itBegin; it != itEnd; ++it ) {
-                Detail::Text usage( it->commands(), Detail::TextAttributes()
-                                                        .setWidth( maxWidth+indent )
-                                                        .setIndent( indent ) );
-                Detail::Text desc( it->description, Detail::TextAttributes()
-                                                        .setWidth( width - maxWidth - 3 ) );
+            struct Contains : StringHolder {
+                Contains( NSString* substr ) : StringHolder( substr ){}
 
-                for( std::size_t i = 0; i < (std::max)( usage.size(), desc.size() ); ++i ) {
-                    std::string usageCol = i < usage.size() ? usage[i] : "";
-                    os << usageCol;
+                bool match( NSString* str ) const override {
+                    return  (str != nil || m_substr == nil ) &&
+                            [str rangeOfString:m_substr].location != NSNotFound;
+                }
 
-                    if( i < desc.size() && !desc[i].empty() )
-                        os  << std::string( indent + 2 + maxWidth - usageCol.size(), ' ' )
-                            << desc[i];
-                    os << "\n";
+                std::string describe() const override {
+                    return "contains string: " + Catch::Detail::stringify( m_substr );
                 }
-            }
-        }
-        std::string optUsage() const {
-            std::ostringstream oss;
-            optUsage( oss );
-            return oss.str();
-        }
-
-        void argSynopsis( std::ostream& os ) const {
-            for( int i = 1; i <= m_highestSpecifiedArgPosition; ++i ) {
-                if( i > 1 )
-                    os << " ";
-                typename std::map<int, Arg>::const_iterator it = m_positionalArgs.find( i );
-                if( it != m_positionalArgs.end() )
-                    os << "<" << it->second.placeholder << ">";
-                else if( m_floatingArg.get() )
-                    os << "<" << m_floatingArg->placeholder << ">";
-                else
-                    throw std::logic_error( "non consecutive positional arguments with no floating args" );
-            }
-            // !TBD No indication of mandatory args
-            if( m_floatingArg.get() ) {
-                if( m_highestSpecifiedArgPosition > 1 )
-                    os << " ";
-                os << "[<" << m_floatingArg->placeholder << "> ...]";
-            }
-        }
-        std::string argSynopsis() const {
-            std::ostringstream oss;
-            argSynopsis( oss );
-            return oss.str();
-        }
+            };
 
-        void usage( std::ostream& os, std::string const& procName ) const {
-            validate();
-            os << "usage:\n  " << procName << " ";
-            argSynopsis( os );
-            if( !m_options.empty() ) {
-                os << " [options]\n\nwhere options are: \n";
-                optUsage( os, 2 );
-            }
-            os << "\n";
-        }
-        std::string usage( std::string const& procName ) const {
-            std::ostringstream oss;
-            usage( oss, procName );
-            return oss.str();
-        }
-
-        ConfigT parse( std::vector<std::string> const& args ) const {
-            ConfigT config;
-            parseInto( args, config );
-            return config;
-        }
-
-        std::vector<Parser::Token> parseInto( std::vector<std::string> const& args, ConfigT& config ) const {
-            std::string processName = args.empty() ? std::string() : args[0];
-            std::size_t lastSlash = processName.find_last_of( "/\\" );
-            if( lastSlash != std::string::npos )
-                processName = processName.substr( lastSlash+1 );
-            m_boundProcessName.set( config, processName );
-            std::vector<Parser::Token> tokens;
-            Parser parser;
-            parser.parseIntoTokens( args, tokens );
-            return populate( tokens, config );
-        }
-
-        std::vector<Parser::Token> populate( std::vector<Parser::Token> const& tokens, ConfigT& config ) const {
-            validate();
-            std::vector<Parser::Token> unusedTokens = populateOptions( tokens, config );
-            unusedTokens = populateFixedArgs( unusedTokens, config );
-            unusedTokens = populateFloatingArgs( unusedTokens, config );
-            return unusedTokens;
-        }
-
-        std::vector<Parser::Token> populateOptions( std::vector<Parser::Token> const& tokens, ConfigT& config ) const {
-            std::vector<Parser::Token> unusedTokens;
-            std::vector<std::string> errors;
-            for( std::size_t i = 0; i < tokens.size(); ++i ) {
-                Parser::Token const& token = tokens[i];
-                typename std::vector<Arg>::const_iterator it = m_options.begin(), itEnd = m_options.end();
-                for(; it != itEnd; ++it ) {
-                    Arg const& arg = *it;
-
-                    try {
-                        if( ( token.type == Parser::Token::ShortOpt && arg.hasShortName( token.data ) ) ||
-                            ( token.type == Parser::Token::LongOpt && arg.hasLongName( token.data ) ) ) {
-                            if( arg.takesArg() ) {
-                                if( i == tokens.size()-1 || tokens[i+1].type != Parser::Token::Positional )
-                                    errors.push_back( "Expected argument to option: " + token.data );
-                                else
-                                    arg.boundField.set( config, tokens[++i].data );
-                            }
-                            else {
-                                arg.boundField.set( config, "true" );
-                            }
-                            break;
-                        }
-                    }
-                    catch( std::exception& ex ) {
-                        errors.push_back( std::string( ex.what() ) + "\n- while parsing: (" + arg.commands() + ")" );
-                    }
+            struct StartsWith : StringHolder {
+                StartsWith( NSString* substr ) : StringHolder( substr ){}
+
+                bool match( NSString* str ) const override {
+                    return  (str != nil || m_substr == nil ) &&
+                            [str rangeOfString:m_substr].location == 0;
                 }
-                if( it == itEnd ) {
-                    if( token.type == Parser::Token::Positional || !m_throwOnUnrecognisedTokens )
-                        unusedTokens.push_back( token );
-                    else if( errors.empty() && m_throwOnUnrecognisedTokens )
-                        errors.push_back( "unrecognised option: " + token.data );
+
+                std::string describe() const override {
+                    return "starts with: " + Catch::Detail::stringify( m_substr );
                 }
-            }
-            if( !errors.empty() ) {
-                std::ostringstream oss;
-                for( std::vector<std::string>::const_iterator it = errors.begin(), itEnd = errors.end();
-                        it != itEnd;
-                        ++it ) {
-                    if( it != errors.begin() )
-                        oss << "\n";
-                    oss << *it;
+            };
+            struct EndsWith : StringHolder {
+                EndsWith( NSString* substr ) : StringHolder( substr ){}
+
+                bool match( NSString* str ) const override {
+                    return  (str != nil || m_substr == nil ) &&
+                            [str rangeOfString:m_substr].location == [str length] - [m_substr length];
                 }
-                throw std::runtime_error( oss.str() );
-            }
-            return unusedTokens;
-        }
-        std::vector<Parser::Token> populateFixedArgs( std::vector<Parser::Token> const& tokens, ConfigT& config ) const {
-            std::vector<Parser::Token> unusedTokens;
-            int position = 1;
-            for( std::size_t i = 0; i < tokens.size(); ++i ) {
-                Parser::Token const& token = tokens[i];
-                typename std::map<int, Arg>::const_iterator it = m_positionalArgs.find( position );
-                if( it != m_positionalArgs.end() )
-                    it->second.boundField.set( config, token.data );
-                else
-                    unusedTokens.push_back( token );
-                if( token.type == Parser::Token::Positional )
-                    position++;
-            }
-            return unusedTokens;
-        }
-        std::vector<Parser::Token> populateFloatingArgs( std::vector<Parser::Token> const& tokens, ConfigT& config ) const {
-            if( !m_floatingArg.get() )
-                return tokens;
-            std::vector<Parser::Token> unusedTokens;
-            for( std::size_t i = 0; i < tokens.size(); ++i ) {
-                Parser::Token const& token = tokens[i];
-                if( token.type == Parser::Token::Positional )
-                    m_floatingArg->boundField.set( config, token.data );
-                else
-                    unusedTokens.push_back( token );
-            }
-            return unusedTokens;
-        }
 
-        void validate() const
-        {
-            if( m_options.empty() && m_positionalArgs.empty() && !m_floatingArg.get() )
-                throw std::logic_error( "No options or arguments specified" );
+                std::string describe() const override {
+                    return "ends with: " + Catch::Detail::stringify( m_substr );
+                }
+            };
 
-            for( typename std::vector<Arg>::const_iterator  it = m_options.begin(),
-                                                            itEnd = m_options.end();
-                    it != itEnd; ++it )
-                it->validate();
-        }
+        } // namespace NSStringMatchers
+        } // namespace Impl
 
-    private:
-        Detail::BoundArgFunction<ConfigT> m_boundProcessName;
-        std::vector<Arg> m_options;
-        std::map<int, Arg> m_positionalArgs;
-        ArgAutoPtr m_floatingArg;
-        int m_highestSpecifiedArgPosition;
-        bool m_throwOnUnrecognisedTokens;
-    };
+        inline Impl::NSStringMatchers::Equals
+            Equals( NSString* substr ){ return Impl::NSStringMatchers::Equals( substr ); }
 
-} // end namespace Clara
+        inline Impl::NSStringMatchers::Contains
+            Contains( NSString* substr ){ return Impl::NSStringMatchers::Contains( substr ); }
 
-STITCH_CLARA_CLOSE_NAMESPACE
-#undef STITCH_CLARA_OPEN_NAMESPACE
-#undef STITCH_CLARA_CLOSE_NAMESPACE
+        inline Impl::NSStringMatchers::StartsWith
+            StartsWith( NSString* substr ){ return Impl::NSStringMatchers::StartsWith( substr ); }
 
-#endif // TWOBLUECUBES_CLARA_H_INCLUDED
-#undef STITCH_CLARA_OPEN_NAMESPACE
+        inline Impl::NSStringMatchers::EndsWith
+            EndsWith( NSString* substr ){ return Impl::NSStringMatchers::EndsWith( substr ); }
 
-// Restore Clara's value for console width, if present
-#ifdef CATCH_TEMP_CLARA_CONFIG_CONSOLE_WIDTH
-#define CLARA_CONFIG_CONSOLE_WIDTH CATCH_TEMP_CLARA_CONFIG_CONSOLE_WIDTH
-#undef CATCH_TEMP_CLARA_CONFIG_CONSOLE_WIDTH
-#endif
+    } // namespace Matchers
 
-#include <fstream>
-#include <ctime>
+    using namespace Matchers;
 
-namespace Catch {
+#endif // CATCH_CONFIG_DISABLE_MATCHERS
 
-    inline void abortAfterFirst( ConfigData& config ) { config.abortAfter = 1; }
-    inline void abortAfterX( ConfigData& config, int x ) {
-        if( x < 1 )
-            throw std::runtime_error( "Value after -x or --abortAfter must be greater than zero" );
-        config.abortAfter = x;
-    }
-    inline void addTestOrTags( ConfigData& config, std::string const& _testSpec ) { config.testsOrTags.push_back( _testSpec ); }
-    inline void addSectionToRun( ConfigData& config, std::string const& sectionName ) { config.sectionsToRun.push_back( sectionName ); }
-    inline void addReporterName( ConfigData& config, std::string const& _reporterName ) { config.reporterNames.push_back( _reporterName ); }
+} // namespace Catch
 
-    inline void addWarning( ConfigData& config, std::string const& _warning ) {
-        if( _warning == "NoAssertions" )
-            config.warnings = static_cast<WarnAbout::What>( config.warnings | WarnAbout::NoAssertions );
-        else
-            throw std::runtime_error( "Unrecognised warning: '" + _warning + '\'' );
-    }
-    inline void setOrder( ConfigData& config, std::string const& order ) {
-        if( startsWith( "declared", order ) )
-            config.runOrder = RunTests::InDeclarationOrder;
-        else if( startsWith( "lexical", order ) )
-            config.runOrder = RunTests::InLexicographicalOrder;
-        else if( startsWith( "random", order ) )
-            config.runOrder = RunTests::InRandomOrder;
-        else
-            throw std::runtime_error( "Unrecognised ordering: '" + order + '\'' );
-    }
-    inline void setRngSeed( ConfigData& config, std::string const& seed ) {
-        if( seed == "time" ) {
-            config.rngSeed = static_cast<unsigned int>( std::time(0) );
-        }
-        else {
-            std::stringstream ss;
-            ss << seed;
-            ss >> config.rngSeed;
-            if( ss.fail() )
-                throw std::runtime_error( "Argument to --rng-seed should be the word 'time' or a number" );
-        }
-    }
-    inline void setVerbosity( ConfigData& config, int level ) {
-        // !TBD: accept strings?
-        config.verbosity = static_cast<Verbosity::Level>( level );
-    }
-    inline void setShowDurations( ConfigData& config, bool _showDurations ) {
-        config.showDurations = _showDurations
-            ? ShowDurations::Always
-            : ShowDurations::Never;
-    }
-    inline void setUseColour( ConfigData& config, std::string const& value ) {
-        std::string mode = toLower( value );
-
-        if( mode == "yes" )
-            config.useColour = UseColour::Yes;
-        else if( mode == "no" )
-            config.useColour = UseColour::No;
-        else if( mode == "auto" )
-            config.useColour = UseColour::Auto;
-        else
-            throw std::runtime_error( "colour mode must be one of: auto, yes or no" );
-    }
-    inline void setWaitForKeypress( ConfigData& config, std::string const& keypress ) {
-        std::string keypressLc = toLower( keypress );
-        if( keypressLc == "start" )
-            config.waitForKeypress = WaitForKeypress::BeforeStart;
-        else if( keypressLc == "exit" )
-            config.waitForKeypress = WaitForKeypress::BeforeExit;
-        else if( keypressLc == "both" )
-            config.waitForKeypress = WaitForKeypress::BeforeStartAndExit;
-        else
-            throw std::runtime_error( "keypress argument must be one of: start, exit or both. '" + keypress + "' not recognised" );
-    };
+///////////////////////////////////////////////////////////////////////////////
+#define OC_MAKE_UNIQUE_NAME( root, uniqueSuffix ) root##uniqueSuffix
+#define OC_TEST_CASE2( name, desc, uniqueSuffix ) \
++(NSString*) OC_MAKE_UNIQUE_NAME( Catch_Name_test_, uniqueSuffix ) \
+{ \
+return @ name; \
+} \
++(NSString*) OC_MAKE_UNIQUE_NAME( Catch_Description_test_, uniqueSuffix ) \
+{ \
+return @ desc; \
+} \
+-(void) OC_MAKE_UNIQUE_NAME( Catch_TestCase_test_, uniqueSuffix )
 
-    inline void forceColour( ConfigData& config ) {
-        config.useColour = UseColour::Yes;
-    }
-    inline void loadTestNamesFromFile( ConfigData& config, std::string const& _filename ) {
-        std::ifstream f( _filename.c_str() );
-        if( !f.is_open() )
-            throw std::domain_error( "Unable to load input file: " + _filename );
+#define OC_TEST_CASE( name, desc ) OC_TEST_CASE2( name, desc, __LINE__ )
 
-        std::string line;
-        while( std::getline( f, line ) ) {
-            line = trim(line);
-            if( !line.empty() && !startsWith( line, '#' ) ) {
-                if( !startsWith( line, '"' ) )
-                    line = '"' + line + '"';
-                addTestOrTags( config, line + ',' );
-            }
-        }
-    }
+// end catch_objc.hpp
+#endif
+
+// 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
 
-    inline Clara::CommandLine<ConfigData> makeCommandLineParser() {
+// start catch_interfaces_reporter.h
 
-        using namespace Clara;
-        CommandLine<ConfigData> cli;
+// start catch_config.hpp
 
-        cli.bindProcessName( &ConfigData::processName );
+// start catch_test_spec_parser.h
 
-        cli["-?"]["-h"]["--help"]
-            .describe( "display usage information" )
-            .bind( &ConfigData::showHelp );
+#ifdef __clang__
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wpadded"
+#endif
+
+// start catch_test_spec.h
+
+#ifdef __clang__
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wpadded"
+#endif
 
-        cli["-l"]["--list-tests"]
-            .describe( "list all/matching test cases" )
-            .bind( &ConfigData::listTests );
+// start catch_wildcard_pattern.h
 
-        cli["-t"]["--list-tags"]
-            .describe( "list all/matching tags" )
-            .bind( &ConfigData::listTags );
+namespace Catch
+{
+    class WildcardPattern {
+        enum WildcardPosition {
+            NoWildcard = 0,
+            WildcardAtStart = 1,
+            WildcardAtEnd = 2,
+            WildcardAtBothEnds = WildcardAtStart | WildcardAtEnd
+        };
 
-        cli["-s"]["--success"]
-            .describe( "include successful tests in output" )
-            .bind( &ConfigData::showSuccessfulTests );
+    public:
 
-        cli["-b"]["--break"]
-            .describe( "break into debugger on failure" )
-            .bind( &ConfigData::shouldDebugBreak );
+        WildcardPattern( std::string const& pattern, CaseSensitive::Choice caseSensitivity );
+        virtual ~WildcardPattern() = default;
+        virtual bool matches( std::string const& str ) const;
 
-        cli["-e"]["--nothrow"]
-            .describe( "skip exception tests" )
-            .bind( &ConfigData::noThrow );
+    private:
+        std::string normaliseString( std::string const& str ) const;
+        CaseSensitive::Choice m_caseSensitivity;
+        WildcardPosition m_wildcard = NoWildcard;
+        std::string m_pattern;
+    };
+}
 
-        cli["-i"]["--invisibles"]
-            .describe( "show invisibles (tabs, newlines)" )
-            .bind( &ConfigData::showInvisibles );
+// end catch_wildcard_pattern.h
+#include <string>
+#include <vector>
+#include <memory>
 
-        cli["-o"]["--out"]
-            .describe( "output filename" )
-            .bind( &ConfigData::outputFilename, "filename" );
+namespace Catch {
 
-        cli["-r"]["--reporter"]
-//            .placeholder( "name[:filename]" )
-            .describe( "reporter to use (defaults to console)" )
-            .bind( &addReporterName, "name" );
+    struct IConfig;
 
-        cli["-n"]["--name"]
-            .describe( "suite name" )
-            .bind( &ConfigData::name, "name" );
+    class TestSpec {
+        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>;
 
-        cli["-a"]["--abort"]
-            .describe( "abort at first failure" )
-            .bind( &abortAfterFirst );
+        class NamePattern : public Pattern {
+        public:
+            explicit NamePattern( std::string const& name, std::string const& filterString );
+            bool matches( TestCaseInfo const& testCase ) const override;
+        private:
+            WildcardPattern m_wildcardPattern;
+        };
 
-        cli["-x"]["--abortx"]
-            .describe( "abort after x failures" )
-            .bind( &abortAfterX, "no. failures" );
+        class TagPattern : public Pattern {
+        public:
+            explicit TagPattern( std::string const& tag, std::string const& filterString );
+            bool matches( TestCaseInfo const& testCase ) const override;
+        private:
+            std::string m_tag;
+        };
 
-        cli["-w"]["--warn"]
-            .describe( "enable warnings" )
-            .bind( &addWarning, "warning name" );
+        class ExcludedPattern : public Pattern {
+        public:
+            explicit ExcludedPattern( PatternPtr const& underlyingPattern );
+            bool matches( TestCaseInfo const& testCase ) const override;
+        private:
+            PatternPtr m_underlyingPattern;
+        };
 
-// - needs updating if reinstated
-//        cli.into( &setVerbosity )
-//            .describe( "level of verbosity (0=no output)" )
-//            .shortOpt( "v")
-//            .longOpt( "verbosity" )
-//            .placeholder( "level" );
+        struct Filter {
+            std::vector<PatternPtr> m_patterns;
 
-        cli[_]
-            .describe( "which test or tests to use" )
-            .bind( &addTestOrTags, "test name, pattern or tags" );
+            bool matches( TestCaseInfo const& testCase ) const;
+            std::string name() const;
+        };
 
-        cli["-d"]["--durations"]
-            .describe( "show test durations" )
-            .bind( &setShowDurations, "yes|no" );
+    public:
+        struct FilterMatch {
+            std::string name;
+            std::vector<TestCase const*> tests;
+        };
+        using Matches = std::vector<FilterMatch>;
+        using vectorStrings = std::vector<std::string>;
 
-        cli["-f"]["--input-file"]
-            .describe( "load test names to run from a file" )
-            .bind( &loadTestNamesFromFile, "filename" );
+        bool hasFilters() const;
+        bool matches( TestCaseInfo const& testCase ) const;
+        Matches matchesByFilter( std::vector<TestCase> const& testCases, IConfig const& config ) const;
+        const vectorStrings & getInvalidArgs() const;
 
-        cli["-#"]["--filenames-as-tags"]
-            .describe( "adds a tag for the filename" )
-            .bind( &ConfigData::filenamesAsTags );
+    private:
+        std::vector<Filter> m_filters;
+        std::vector<std::string> m_invalidArgs;
+        friend class TestSpecParser;
+    };
+}
 
-        cli["-c"]["--section"]
-                .describe( "specify section to run" )
-                .bind( &addSectionToRun, "section name" );
+#ifdef __clang__
+#pragma clang diagnostic pop
+#endif
 
-        // Less common commands which don't have a short form
-        cli["--list-test-names-only"]
-            .describe( "list all/matching test cases names only" )
-            .bind( &ConfigData::listTestNamesOnly );
+// end catch_test_spec.h
+// start catch_interfaces_tag_alias_registry.h
 
-        cli["--list-extra-info"]
-            .describe( "list all/matching test cases with more info" )
-            .bind( &ConfigData::listExtraInfo );
+#include <string>
 
-        cli["--list-reporters"]
-            .describe( "list all reporters" )
-            .bind( &ConfigData::listReporters );
+namespace Catch {
 
-        cli["--order"]
-            .describe( "test case order (defaults to decl)" )
-            .bind( &setOrder, "decl|lex|rand" );
+    struct TagAlias;
 
-        cli["--rng-seed"]
-            .describe( "set a specific seed for random numbers" )
-            .bind( &setRngSeed, "'time'|number" );
-
-        cli["--force-colour"]
-            .describe( "force colourised output (deprecated)" )
-            .bind( &forceColour );
-
-        cli["--use-colour"]
-            .describe( "should output be colourised" )
-            .bind( &setUseColour, "yes|no" );
-
-        cli["--libidentify"]
-            .describe( "report name and version according to libidentify standard" )
-            .bind( &ConfigData::libIdentify );
-
-        cli["--wait-for-keypress"]
-                .describe( "waits for a keypress before exiting" )
-                .bind( &setWaitForKeypress, "start|exit|both" );
+    struct ITagAliasRegistry {
+        virtual ~ITagAliasRegistry();
+        // Nullptr if not present
+        virtual TagAlias const* find( std::string const& alias ) const = 0;
+        virtual std::string expandAliases( std::string const& unexpandedTestSpec ) const = 0;
 
-        return cli;
-    }
+        static ITagAliasRegistry const& get();
+    };
 
 } // end namespace Catch
 
-// #included from: internal/catch_list.hpp
-#define TWOBLUECUBES_CATCH_LIST_HPP_INCLUDED
+// end catch_interfaces_tag_alias_registry.h
+namespace Catch {
+
+    class TestSpecParser {
+        enum Mode{ None, Name, QuotedName, Tag, EscapedName };
+        Mode m_mode = None;
+        Mode lastMode = None;
+        bool m_exclusion = false;
+        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;
+        ITagAliasRegistry const* m_tagAliases = nullptr;
+
+    public:
+        TestSpecParser( ITagAliasRegistry const& tagAliases );
 
-// #included from: catch_text.h
-#define TWOBLUECUBES_CATCH_TEXT_H_INCLUDED
+        TestSpecParser& parse( std::string const& arg );
+        TestSpec testSpec();
 
-#define TBC_TEXT_FORMAT_CONSOLE_WIDTH CATCH_CONFIG_CONSOLE_WIDTH
+    private:
+        bool visitChar( char c );
+        void startNewMode( Mode mode );
+        bool processNoneChar( char c );
+        void processNameChar( char c );
+        bool processOtherChar( char c );
+        void endMode();
+        void escape();
+        bool isControlChar( char c ) const;
+        void saveLastMode();
+        void revertBackToLastMode();
+        void addFilter();
+        bool separate();
+
+        // 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++;
+        }
 
-#define CLICHE_TBC_TEXT_FORMAT_OUTER_NAMESPACE Catch
-// #included from: ../external/tbc_text_format.h
-// Only use header guard if we are not using an outer namespace
-#ifndef CLICHE_TBC_TEXT_FORMAT_OUTER_NAMESPACE
-# ifdef TWOBLUECUBES_TEXT_FORMAT_H_INCLUDED
-#  ifndef TWOBLUECUBES_TEXT_FORMAT_H_ALREADY_INCLUDED
-#   define TWOBLUECUBES_TEXT_FORMAT_H_ALREADY_INCLUDED
-#  endif
-# else
-#  define TWOBLUECUBES_TEXT_FORMAT_H_INCLUDED
-# endif
+    };
+    TestSpec parseTestSpec( std::string const& arg );
+
+} // namespace Catch
+
+#ifdef __clang__
+#pragma clang diagnostic pop
 #endif
-#ifndef TWOBLUECUBES_TEXT_FORMAT_H_ALREADY_INCLUDED
-#include <string>
+
+// end catch_test_spec_parser.h
+// Libstdc++ doesn't like incomplete classes for unique_ptr
+
+#include <memory>
 #include <vector>
-#include <sstream>
+#include <string>
 
-// Use optional outer namespace
-#ifdef CLICHE_TBC_TEXT_FORMAT_OUTER_NAMESPACE
-namespace CLICHE_TBC_TEXT_FORMAT_OUTER_NAMESPACE {
+#ifndef CATCH_CONFIG_CONSOLE_WIDTH
+#define CATCH_CONFIG_CONSOLE_WIDTH 80
 #endif
 
-namespace Tbc {
+namespace Catch {
 
-#ifdef TBC_TEXT_FORMAT_CONSOLE_WIDTH
-    const unsigned int consoleWidth = TBC_TEXT_FORMAT_CONSOLE_WIDTH;
-#else
-    const unsigned int consoleWidth = 80;
-#endif
+    struct IStream;
 
-    struct TextAttributes {
-        TextAttributes()
-        :   initialIndent( std::string::npos ),
-            indent( 0 ),
-            width( consoleWidth-1 )
-        {}
+    struct ConfigData {
+        bool listTests = false;
+        bool listTags = false;
+        bool listReporters = false;
+        bool listTestNamesOnly = false;
+
+        bool showSuccessfulTests = false;
+        bool shouldDebugBreak = false;
+        bool noThrow = false;
+        bool showHelp = false;
+        bool showInvisibles = false;
+        bool filenamesAsTags = false;
+        bool libIdentify = false;
+
+        int abortAfter = -1;
+        unsigned int rngSeed = 0;
+
+        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;
 
-        TextAttributes& setInitialIndent( std::size_t _value )  { initialIndent = _value; return *this; }
-        TextAttributes& setIndent( std::size_t _value )         { indent = _value; return *this; }
-        TextAttributes& setWidth( std::size_t _value )          { width = _value; return *this; }
+        std::string outputFilename;
+        std::string name;
+        std::string processName;
+#ifndef CATCH_CONFIG_DEFAULT_REPORTER
+#define CATCH_CONFIG_DEFAULT_REPORTER "console"
+#endif
+        std::string reporterName = CATCH_CONFIG_DEFAULT_REPORTER;
+#undef CATCH_CONFIG_DEFAULT_REPORTER
 
-        std::size_t initialIndent;  // indent of first line, or npos
-        std::size_t indent;         // indent of subsequent lines, or all if initialIndent is npos
-        std::size_t width;          // maximum width of text, including indent. Longer text will wrap
+        std::vector<std::string> testsOrTags;
+        std::vector<std::string> sectionsToRun;
     };
 
-    class Text {
+    class Config : public IConfig {
     public:
-        Text( std::string const& _str, TextAttributes const& _attr = TextAttributes() )
-        : attr( _attr )
-        {
-            const std::string wrappableBeforeChars = "[({<\t";
-            const std::string wrappableAfterChars = "])}>-,./|\\";
-            const std::string wrappableInsteadOfChars = " \n\r";
-            std::string indent = _attr.initialIndent != std::string::npos
-                ? std::string( _attr.initialIndent, ' ' )
-                : std::string( _attr.indent, ' ' );
-
-            typedef std::string::const_iterator iterator;
-            iterator it = _str.begin();
-            const iterator strEnd = _str.end();
-
-            while( it != strEnd ) {
-
-                if( lines.size() >= 1000 ) {
-                    lines.push_back( "... message truncated due to excessive size" );
-                    return;
-                }
 
-                std::string suffix;
-                std::size_t width = (std::min)( static_cast<size_t>( strEnd-it ), _attr.width-static_cast<size_t>( indent.size() ) );
-                iterator itEnd = it+width;
-                iterator itNext = _str.end();
-
-                iterator itNewLine = std::find( it, itEnd, '\n' );
-                if( itNewLine != itEnd )
-                    itEnd = itNewLine;
-
-                if( itEnd != strEnd  ) {
-                    bool foundWrapPoint = false;
-                    iterator findIt = itEnd;
-                    do {
-                        if( wrappableAfterChars.find( *findIt ) != std::string::npos && findIt != itEnd ) {
-                            itEnd = findIt+1;
-                            itNext = findIt+1;
-                            foundWrapPoint = true;
-                        }
-                        else if( findIt > it && wrappableBeforeChars.find( *findIt ) != std::string::npos ) {
-                            itEnd = findIt;
-                            itNext = findIt;
-                            foundWrapPoint = true;
-                        }
-                        else if( wrappableInsteadOfChars.find( *findIt ) != std::string::npos ) {
-                            itNext = findIt+1;
-                            itEnd = findIt;
-                            foundWrapPoint = true;
-                        }
-                        if( findIt == it )
-                            break;
-                        else
-                            --findIt;
-                    }
-                    while( !foundWrapPoint );
+        Config() = default;
+        Config( ConfigData const& data );
+        virtual ~Config() = default;
 
-                    if( !foundWrapPoint ) {
-                        // No good wrap char, so we'll break mid word and add a hyphen
-                        --itEnd;
-                        itNext = itEnd;
-                        suffix = "-";
-                    }
-                    else {
-                        while( itEnd > it && wrappableInsteadOfChars.find( *(itEnd-1) ) != std::string::npos )
-                            --itEnd;
-                    }
-                }
-                lines.push_back( indent + std::string( it, itEnd ) + suffix );
+        std::string const& getFilename() const;
 
-                if( indent.size() != _attr.indent )
-                    indent = std::string( _attr.indent, ' ' );
-                it = itNext;
-            }
-        }
+        bool listTests() const;
+        bool listTestNamesOnly() const;
+        bool listTags() const;
+        bool listReporters() const;
 
-        typedef std::vector<std::string>::const_iterator const_iterator;
+        std::string getProcessName() const;
+        std::string const& getReporterName() const;
 
-        const_iterator begin() const { return lines.begin(); }
-        const_iterator end() const { return lines.end(); }
-        std::string const& last() const { return lines.back(); }
-        std::size_t size() const { return lines.size(); }
-        std::string const& operator[]( std::size_t _index ) const { return lines[_index]; }
-        std::string toString() const {
-            std::ostringstream oss;
-            oss << *this;
-            return oss.str();
-        }
+        std::vector<std::string> const& getTestsOrTags() const override;
+        std::vector<std::string> const& getSectionsToRun() const override;
 
-        inline friend std::ostream& operator << ( std::ostream& _stream, Text const& _text ) {
-            for( Text::const_iterator it = _text.begin(), itEnd = _text.end();
-                it != itEnd; ++it ) {
-                if( it != _text.begin() )
-                    _stream << "\n";
-                _stream << *it;
-            }
-            return _stream;
-        }
+        TestSpec const& testSpec() const override;
+        bool hasTestFilters() const override;
+
+        bool showHelp() const;
+
+        // IConfig interface
+        bool allowThrows() const override;
+        std::ostream& stream() const override;
+        std::string name() const override;
+        bool includeSuccessfulResults() const override;
+        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;
+        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:
-        std::string str;
-        TextAttributes attr;
-        std::vector<std::string> lines;
+
+        IStream const* openStream();
+        ConfigData m_data;
+
+        std::unique_ptr<IStream const> m_stream;
+        TestSpec m_testSpec;
+        bool m_hasTestFilters = false;
     };
 
-} // end namespace Tbc
+} // end namespace Catch
 
-#ifdef CLICHE_TBC_TEXT_FORMAT_OUTER_NAMESPACE
-} // end outer namespace
-#endif
+// end catch_config.hpp
+// start catch_assertionresult.h
 
-#endif // TWOBLUECUBES_TEXT_FORMAT_H_ALREADY_INCLUDED
-#undef CLICHE_TBC_TEXT_FORMAT_OUTER_NAMESPACE
+#include <string>
 
 namespace Catch {
-    using Tbc::Text;
-    using Tbc::TextAttributes;
-}
 
-// #included from: catch_console_colour.hpp
-#define TWOBLUECUBES_CATCH_CONSOLE_COLOUR_HPP_INCLUDED
+    struct AssertionResultData
+    {
+        AssertionResultData() = delete;
 
-namespace Catch {
+        AssertionResultData( ResultWas::OfType _resultType, LazyExpression const& _lazyExpression );
 
-    struct Colour {
-        enum Code {
-            None = 0,
+        std::string message;
+        mutable std::string reconstructedExpression;
+        LazyExpression lazyExpression;
+        ResultWas::OfType resultType;
 
-            White,
-            Red,
-            Green,
-            Blue,
-            Cyan,
-            Yellow,
-            Grey,
+        std::string reconstructExpression() const;
+    };
 
-            Bright = 0x10,
+    class AssertionResult {
+    public:
+        AssertionResult() = delete;
+        AssertionResult( AssertionInfo const& info, AssertionResultData const& data );
 
-            BrightRed = Bright | Red,
-            BrightGreen = Bright | Green,
-            LightGrey = Bright | Grey,
-            BrightWhite = Bright | White,
+        bool isOk() const;
+        bool succeeded() const;
+        ResultWas::OfType getResultType() const;
+        bool hasExpression() const;
+        bool hasMessage() const;
+        std::string getExpression() const;
+        std::string getExpressionInMacro() const;
+        bool hasExpandedExpression() const;
+        std::string getExpandedExpression() const;
+        std::string getMessage() const;
+        SourceLineInfo getSourceInfo() const;
+        StringRef getTestMacroName() const;
 
-            // By intention
-            FileName = LightGrey,
-            Warning = Yellow,
-            ResultError = BrightRed,
-            ResultSuccess = BrightGreen,
-            ResultExpectedFailure = Warning,
+    //protected:
+        AssertionInfo m_info;
+        AssertionResultData m_resultData;
+    };
 
-            Error = BrightRed,
-            Success = Green,
+} // end namespace Catch
 
-            OriginalExpression = Cyan,
-            ReconstructedExpression = Yellow,
+// end catch_assertionresult.h
+#if defined(CATCH_CONFIG_ENABLE_BENCHMARKING)
+// start catch_estimate.hpp
 
-            SecondaryText = LightGrey,
-            Headers = White
-        };
+ // Statistics estimates
 
-        // Use constructed object for RAII guard
-        Colour( Code _colourCode );
-        Colour( Colour const& other );
-        ~Colour();
 
-        // Use static method for one-shot changes
-        static void use( Code _colourCode );
+namespace Catch {
+    namespace Benchmark {
+        template <typename Duration>
+        struct Estimate {
+            Duration point;
+            Duration lower_bound;
+            Duration upper_bound;
+            double confidence_interval;
+
+            template <typename Duration2>
+            operator Estimate<Duration2>() const {
+                return { point, lower_bound, upper_bound, confidence_interval };
+            }
+        };
+    } // namespace Benchmark
+} // namespace Catch
 
-    private:
-        bool m_moved;
-    };
+// end catch_estimate.hpp
+// start catch_outlier_classification.hpp
 
-    inline std::ostream& operator << ( std::ostream& os, Colour const& ) { return os; }
+// Outlier information
 
-} // end namespace Catch
+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
+
+            int total() const {
+                return low_severe + low_mild + high_mild + high_severe;
+            }
+        };
+    } // namespace Benchmark
+} // namespace Catch
 
-// #included from: catch_interfaces_reporter.h
-#define TWOBLUECUBES_CATCH_INTERFACES_REPORTER_H_INCLUDED
+// end catch_outlier_classification.hpp
+
+#include <iterator>
+#endif // CATCH_CONFIG_ENABLE_BENCHMARKING
 
 #include <string>
-#include <ostream>
+#include <iosfwd>
 #include <map>
+#include <set>
+#include <memory>
+#include <algorithm>
+
+namespace Catch {
 
-namespace Catch
-{
     struct ReporterConfig {
-        explicit ReporterConfig( Ptr<IConfig const> const& _fullConfig )
-        :   m_stream( &_fullConfig->stream() ), m_fullConfig( _fullConfig ) {}
+        explicit ReporterConfig( IConfigPtr const& _fullConfig );
 
-        ReporterConfig( Ptr<IConfig const> const& _fullConfig, std::ostream& _stream )
-        :   m_stream( &_stream ), m_fullConfig( _fullConfig ) {}
+        ReporterConfig( IConfigPtr const& _fullConfig, std::ostream& _stream );
 
-        std::ostream& stream() const    { return *m_stream; }
-        Ptr<IConfig const> fullConfig() const { return m_fullConfig; }
+        std::ostream& stream() const;
+        IConfigPtr fullConfig() const;
 
     private:
         std::ostream* m_stream;
-        Ptr<IConfig const> m_fullConfig;
+        IConfigPtr m_fullConfig;
     };
 
     struct ReporterPreferences {
-        ReporterPreferences()
-        : shouldRedirectStdOut( false )
-        {}
-
-        bool shouldRedirectStdOut;
+        bool shouldRedirectStdOut = false;
+        bool shouldReportAllAssertions = false;
     };
 
     template<typename T>
     struct LazyStat : Option<T> {
-        LazyStat() : used( false ) {}
         LazyStat& operator=( T const& _value ) {
             Option<T>::operator=( _value );
             used = false;
@@ -5643,21 +5507,17 @@ namespace Catch
             Option<T>::reset();
             used = false;
         }
-        bool used;
+        bool used = false;
     };
 
     struct TestRunInfo {
-        TestRunInfo( std::string const& _name ) : name( _name ) {}
+        TestRunInfo( std::string const& _name );
         std::string name;
     };
     struct GroupInfo {
         GroupInfo(  std::string const& _name,
                     std::size_t _groupIndex,
-                    std::size_t _groupsCount )
-        :   name( _name ),
-            groupIndex( _groupIndex ),
-            groupsCounts( _groupsCount )
-        {}
+                    std::size_t _groupsCount );
 
         std::string name;
         std::size_t groupIndex;
@@ -5667,29 +5527,13 @@ namespace Catch
     struct AssertionStats {
         AssertionStats( AssertionResult const& _assertionResult,
                         std::vector<MessageInfo> const& _infoMessages,
-                        Totals const& _totals )
-        :   assertionResult( _assertionResult ),
-            infoMessages( _infoMessages ),
-            totals( _totals )
-        {
-            if( assertionResult.hasMessage() ) {
-                // Copy message into messages list.
-                // !TBD This should have been done earlier, somewhere
-                MessageBuilder builder( assertionResult.getTestMacroName(), assertionResult.getSourceInfo(), assertionResult.getResultType() );
-                builder << assertionResult.getMessage();
-                builder.m_info.message = builder.m_stream.str();
-
-                infoMessages.push_back( builder.m_info );
-            }
-        }
-        virtual ~AssertionStats();
+                        Totals const& _totals );
 
-#  ifdef CATCH_CONFIG_CPP11_GENERATED_METHODS
         AssertionStats( AssertionStats const& )              = default;
         AssertionStats( AssertionStats && )                  = default;
-        AssertionStats& operator = ( AssertionStats const& ) = default;
-        AssertionStats& operator = ( AssertionStats && )     = default;
-#  endif
+        AssertionStats& operator = ( AssertionStats const& ) = delete;
+        AssertionStats& operator = ( AssertionStats && )     = delete;
+        virtual ~AssertionStats();
 
         AssertionResult assertionResult;
         std::vector<MessageInfo> infoMessages;
@@ -5700,19 +5544,12 @@ namespace Catch
         SectionStats(   SectionInfo const& _sectionInfo,
                         Counts const& _assertions,
                         double _durationInSeconds,
-                        bool _missingAssertions )
-        :   sectionInfo( _sectionInfo ),
-            assertions( _assertions ),
-            durationInSeconds( _durationInSeconds ),
-            missingAssertions( _missingAssertions )
-        {}
-        virtual ~SectionStats();
-#  ifdef CATCH_CONFIG_CPP11_GENERATED_METHODS
+                        bool _missingAssertions );
         SectionStats( SectionStats const& )              = default;
         SectionStats( SectionStats && )                  = default;
         SectionStats& operator = ( SectionStats const& ) = default;
         SectionStats& operator = ( SectionStats && )     = default;
-#  endif
+        virtual ~SectionStats();
 
         SectionInfo sectionInfo;
         Counts assertions;
@@ -5725,21 +5562,13 @@ namespace Catch
                         Totals const& _totals,
                         std::string const& _stdOut,
                         std::string const& _stdErr,
-                        bool _aborting )
-        : testInfo( _testInfo ),
-            totals( _totals ),
-            stdOut( _stdOut ),
-            stdErr( _stdErr ),
-            aborting( _aborting )
-        {}
-        virtual ~TestCaseStats();
+                        bool _aborting );
 
-#  ifdef CATCH_CONFIG_CPP11_GENERATED_METHODS
         TestCaseStats( TestCaseStats const& )              = default;
         TestCaseStats( TestCaseStats && )                  = default;
         TestCaseStats& operator = ( TestCaseStats const& ) = default;
         TestCaseStats& operator = ( TestCaseStats && )     = default;
-#  endif
+        virtual ~TestCaseStats();
 
         TestCaseInfo testInfo;
         Totals totals;
@@ -5751,23 +5580,14 @@ namespace Catch
     struct TestGroupStats {
         TestGroupStats( GroupInfo const& _groupInfo,
                         Totals const& _totals,
-                        bool _aborting )
-        :   groupInfo( _groupInfo ),
-            totals( _totals ),
-            aborting( _aborting )
-        {}
-        TestGroupStats( GroupInfo const& _groupInfo )
-        :   groupInfo( _groupInfo ),
-            aborting( false )
-        {}
-        virtual ~TestGroupStats();
+                        bool _aborting );
+        TestGroupStats( GroupInfo const& _groupInfo );
 
-#  ifdef CATCH_CONFIG_CPP11_GENERATED_METHODS
         TestGroupStats( TestGroupStats const& )              = default;
         TestGroupStats( TestGroupStats && )                  = default;
         TestGroupStats& operator = ( TestGroupStats const& ) = default;
         TestGroupStats& operator = ( TestGroupStats && )     = default;
-#  endif
+        virtual ~TestGroupStats();
 
         GroupInfo groupInfo;
         Totals totals;
@@ -5777,49 +5597,83 @@ namespace Catch
     struct TestRunStats {
         TestRunStats(   TestRunInfo const& _runInfo,
                         Totals const& _totals,
-                        bool _aborting )
-        :   runInfo( _runInfo ),
-            totals( _totals ),
-            aborting( _aborting )
-        {}
-        virtual ~TestRunStats();
+                        bool _aborting );
 
-#  ifndef CATCH_CONFIG_CPP11_GENERATED_METHODS
-        TestRunStats( TestRunStats const& _other )
-        :   runInfo( _other.runInfo ),
-            totals( _other.totals ),
-            aborting( _other.aborting )
-        {}
-#  else
         TestRunStats( TestRunStats const& )              = default;
         TestRunStats( TestRunStats && )                  = default;
         TestRunStats& operator = ( TestRunStats const& ) = default;
         TestRunStats& operator = ( TestRunStats && )     = default;
-#  endif
+        virtual ~TestRunStats();
 
         TestRunInfo runInfo;
         Totals totals;
         bool aborting;
     };
 
-    class MultipleReporters;
+#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::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 : IShared {
-        virtual ~IStreamingReporter();
+    struct IStreamingReporter {
+        virtual ~IStreamingReporter() = default;
 
-        // Implementing class must also provide the following static method:
+        // Implementing class must also provide the following static methods:
         // static std::string getDescription();
+        // static std::set<Verbosity> getSupportedVerbosities()
 
         virtual ReporterPreferences getPreferences() const = 0;
 
         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;
 
+#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:
@@ -5832,2475 +5686,8383 @@ namespace Catch
 
         virtual void skipTest( TestCaseInfo const& testInfo ) = 0;
 
-        virtual MultipleReporters* tryAsMulti() { return CATCH_NULL; }
+        // Default empty implementation provided
+        virtual void fatalErrorEncountered( StringRef name );
+
+        virtual bool isMulti() const;
     };
+    using IStreamingReporterPtr = std::unique_ptr<IStreamingReporter>;
 
-    struct IReporterFactory : IShared {
+    struct IReporterFactory {
         virtual ~IReporterFactory();
-        virtual IStreamingReporter* create( ReporterConfig const& config ) const = 0;
+        virtual IStreamingReporterPtr create( ReporterConfig const& config ) const = 0;
         virtual std::string getDescription() const = 0;
     };
+    using IReporterFactoryPtr = std::shared_ptr<IReporterFactory>;
 
     struct IReporterRegistry {
-        typedef std::map<std::string, Ptr<IReporterFactory> > FactoryMap;
-        typedef std::vector<Ptr<IReporterFactory> > Listeners;
+        using FactoryMap = std::map<std::string, IReporterFactoryPtr>;
+        using Listeners = std::vector<IReporterFactoryPtr>;
 
         virtual ~IReporterRegistry();
-        virtual IStreamingReporter* create( std::string const& name, Ptr<IConfig const> const& config ) const = 0;
+        virtual IStreamingReporterPtr create( std::string const& name, IConfigPtr const& config ) const = 0;
         virtual FactoryMap const& getFactories() const = 0;
         virtual Listeners const& getListeners() const = 0;
     };
 
-    Ptr<IStreamingReporter> addReporter( Ptr<IStreamingReporter> const& existingReporter, Ptr<IStreamingReporter> const& additionalReporter );
-
-}
+} // end namespace Catch
 
-#include <limits>
+// end catch_interfaces_reporter.h
 #include <algorithm>
+#include <cstring>
+#include <cfloat>
+#include <cstdio>
+#include <cassert>
+#include <memory>
+#include <ostream>
 
 namespace Catch {
+    void prepareExpandedExpression(AssertionResult& result);
 
-    inline std::size_t listTests( Config const& config ) {
+    // Returns double formatted as %.3f (format expected on output)
+    std::string getFormattedDuration( double duration );
 
-        TestSpec testSpec = config.testSpec();
-        if( config.testSpec().hasFilters() )
-            Catch::cout() << "Matching test cases:\n";
-        else {
-            Catch::cout() << "All available test cases:\n";
-            testSpec = TestSpecParser( ITagAliasRegistry::get() ).parse( "*" ).testSpec();
-        }
+    //! Should the reporter show
+    bool shouldShowDuration( IConfig const& config, double duration );
 
-        std::size_t matchedTests = 0;
-        TextAttributes nameAttr, descAttr, tagsAttr;
-        nameAttr.setInitialIndent( 2 ).setIndent( 4 );
-        descAttr.setIndent( 4 );
-        tagsAttr.setIndent( 6 );
+    std::string serializeFilters( std::vector<std::string> const& container );
 
-        std::vector<TestCase> matchedTestCases = filterTests( getAllTestCasesSorted( config ), testSpec, config );
-        for( std::vector<TestCase>::const_iterator it = matchedTestCases.begin(), itEnd = matchedTestCases.end();
-                it != itEnd;
-                ++it ) {
-            matchedTests++;
-            TestCaseInfo const& testCaseInfo = it->getTestCaseInfo();
-            Colour::Code colour = testCaseInfo.isHidden()
-                ? Colour::SecondaryText
-                : Colour::None;
-            Colour colourGuard( colour );
+    template<typename DerivedT>
+    struct StreamingReporterBase : IStreamingReporter {
 
-            Catch::cout() << Text( testCaseInfo.name, nameAttr ) << std::endl;
-            if( config.listExtraInfo() ) {
-                Catch::cout() << "    " << testCaseInfo.lineInfo << std::endl;
-                std::string description = testCaseInfo.description;
-                if( description.empty() )
-                    description = "(NO DESCRIPTION)";
-                Catch::cout() << Text( description, descAttr ) << std::endl;
-            }
-            if( !testCaseInfo.tags.empty() )
-                Catch::cout() << Text( testCaseInfo.tagsAsString, tagsAttr ) << std::endl;
+        StreamingReporterBase( ReporterConfig const& _config )
+        :   m_config( _config.fullConfig() ),
+            stream( _config.stream() )
+        {
+            m_reporterPrefs.shouldRedirectStdOut = false;
+            if( !DerivedT::getSupportedVerbosities().count( m_config->verbosity() ) )
+                CATCH_ERROR( "Verbosity level not supported by this reporter" );
         }
 
-        if( !config.testSpec().hasFilters() )
-            Catch::cout() << pluralise( matchedTests, "test case" ) << '\n' << std::endl;
-        else
-            Catch::cout() << pluralise( matchedTests, "matching test case" ) << '\n' << std::endl;
-        return matchedTests;
-    }
-
-    inline std::size_t listTestsNamesOnly( Config const& config ) {
-        TestSpec testSpec = config.testSpec();
-        if( !config.testSpec().hasFilters() )
-            testSpec = TestSpecParser( ITagAliasRegistry::get() ).parse( "*" ).testSpec();
-        std::size_t matchedTests = 0;
-        std::vector<TestCase> matchedTestCases = filterTests( getAllTestCasesSorted( config ), testSpec, config );
-        for( std::vector<TestCase>::const_iterator it = matchedTestCases.begin(), itEnd = matchedTestCases.end();
-                it != itEnd;
-                ++it ) {
-            matchedTests++;
-            TestCaseInfo const& testCaseInfo = it->getTestCaseInfo();
-            if( startsWith( testCaseInfo.name, '#' ) )
-               Catch::cout() << '"' << testCaseInfo.name << '"';
-            else
-               Catch::cout() << testCaseInfo.name;
-            if ( config.listExtraInfo() )
-                Catch::cout() << "\t@" << testCaseInfo.lineInfo;
-            Catch::cout() << std::endl;
+        ReporterPreferences getPreferences() const override {
+            return m_reporterPrefs;
         }
-        return matchedTests;
-    }
 
-    struct TagInfo {
-        TagInfo() : count ( 0 ) {}
-        void add( std::string const& spelling ) {
-            ++count;
-            spellings.insert( spelling );
-        }
-        std::string all() const {
-            std::string out;
-            for( std::set<std::string>::const_iterator it = spellings.begin(), itEnd = spellings.end();
-                        it != itEnd;
-                        ++it )
-                out += "[" + *it + "]";
-            return out;
+        static std::set<Verbosity> getSupportedVerbosities() {
+            return { Verbosity::Normal };
         }
-        std::set<std::string> spellings;
-        std::size_t count;
-    };
 
-    inline std::size_t listTags( Config const& config ) {
-        TestSpec testSpec = config.testSpec();
-        if( config.testSpec().hasFilters() )
-            Catch::cout() << "Tags for matching test cases:\n";
-        else {
-            Catch::cout() << "All available tags:\n";
-            testSpec = TestSpecParser( ITagAliasRegistry::get() ).parse( "*" ).testSpec();
-        }
+        ~StreamingReporterBase() override = default;
 
-        std::map<std::string, TagInfo> tagCounts;
+        void noMatchingTestCases(std::string const&) override {}
 
-        std::vector<TestCase> matchedTestCases = filterTests( getAllTestCasesSorted( config ), testSpec, config );
-        for( std::vector<TestCase>::const_iterator it = matchedTestCases.begin(), itEnd = matchedTestCases.end();
-                it != itEnd;
-                ++it ) {
-            for( std::set<std::string>::const_iterator  tagIt = it->getTestCaseInfo().tags.begin(),
-                                                        tagItEnd = it->getTestCaseInfo().tags.end();
-                    tagIt != tagItEnd;
-                    ++tagIt ) {
-                std::string tagName = *tagIt;
-                std::string lcaseTagName = toLower( tagName );
-                std::map<std::string, TagInfo>::iterator countIt = tagCounts.find( lcaseTagName );
-                if( countIt == tagCounts.end() )
-                    countIt = tagCounts.insert( std::make_pair( lcaseTagName, TagInfo() ) ).first;
-                countIt->second.add( tagName );
-            }
-        }
+        void reportInvalidArguments(std::string const&) override {}
 
-        for( std::map<std::string, TagInfo>::const_iterator countIt = tagCounts.begin(),
-                                                            countItEnd = tagCounts.end();
-                countIt != countItEnd;
-                ++countIt ) {
-            std::ostringstream oss;
-            oss << "  " << std::setw(2) << countIt->second.count << "  ";
-            Text wrapper( countIt->second.all(), TextAttributes()
-                                                    .setInitialIndent( 0 )
-                                                    .setIndent( oss.str().size() )
-                                                    .setWidth( CATCH_CONFIG_CONSOLE_WIDTH-10 ) );
-            Catch::cout() << oss.str() << wrapper << '\n';
+        void testRunStarting(TestRunInfo const& _testRunInfo) override {
+            currentTestRunInfo = _testRunInfo;
         }
-        Catch::cout() << pluralise( tagCounts.size(), "tag" ) << '\n' << std::endl;
-        return tagCounts.size();
-    }
 
-    inline std::size_t listReporters( Config const& /*config*/ ) {
-        Catch::cout() << "Available reporters:\n";
-        IReporterRegistry::FactoryMap const& factories = getRegistryHub().getReporterRegistry().getFactories();
-        IReporterRegistry::FactoryMap::const_iterator itBegin = factories.begin(), itEnd = factories.end(), it;
-        std::size_t maxNameLen = 0;
-        for(it = itBegin; it != itEnd; ++it )
-            maxNameLen = (std::max)( maxNameLen, it->first.size() );
-
-        for(it = itBegin; it != itEnd; ++it ) {
-            Text wrapper( it->second->getDescription(), TextAttributes()
-                                                        .setInitialIndent( 0 )
-                                                        .setIndent( 7+maxNameLen )
-                                                        .setWidth( CATCH_CONFIG_CONSOLE_WIDTH - maxNameLen-8 ) );
-            Catch::cout() << "  "
-                    << it->first
-                    << ':'
-                    << std::string( maxNameLen - it->first.size() + 2, ' ' )
-                    << wrapper << '\n';
+        void testGroupStarting(GroupInfo const& _groupInfo) override {
+            currentGroupInfo = _groupInfo;
         }
-        Catch::cout() << std::endl;
-        return factories.size();
-    }
 
-    inline Option<std::size_t> list( Config const& config ) {
-        Option<std::size_t> listedCount;
-        if( config.listTests() || ( config.listExtraInfo() && !config.listTestNamesOnly() ) )
-            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( config );
-        return listedCount;
-    }
-
-} // end namespace Catch
-
-// #included from: internal/catch_run_context.hpp
-#define TWOBLUECUBES_CATCH_RUNNER_IMPL_HPP_INCLUDED
-
-// #included from: catch_test_case_tracker.hpp
-#define TWOBLUECUBES_CATCH_TEST_CASE_TRACKER_HPP_INCLUDED
+        void testCaseStarting(TestCaseInfo const& _testInfo) override  {
+            currentTestCaseInfo = _testInfo;
+        }
+        void sectionStarting(SectionInfo const& _sectionInfo) override {
+            m_sectionStack.push_back(_sectionInfo);
+        }
 
-#include <algorithm>
-#include <string>
-#include <assert.h>
-#include <vector>
-#include <stdexcept>
+        void sectionEnded(SectionStats const& /* _sectionStats */) override {
+            m_sectionStack.pop_back();
+        }
+        void testCaseEnded(TestCaseStats const& /* _testCaseStats */) override {
+            currentTestCaseInfo.reset();
+        }
+        void testGroupEnded(TestGroupStats const& /* _testGroupStats */) override {
+            currentGroupInfo.reset();
+        }
+        void testRunEnded(TestRunStats const& /* _testRunStats */) override {
+            currentTestCaseInfo.reset();
+            currentGroupInfo.reset();
+            currentTestRunInfo.reset();
+        }
 
-CATCH_INTERNAL_SUPPRESS_ETD_WARNINGS
+        void skipTest(TestCaseInfo const&) override {
+            // Don't do anything with this by default.
+            // It can optionally be overridden in the derived class.
+        }
 
-namespace Catch {
-namespace TestCaseTracking {
+        IConfigPtr m_config;
+        std::ostream& stream;
 
-    struct NameAndLocation {
-        std::string name;
-        SourceLineInfo location;
+        LazyStat<TestRunInfo> currentTestRunInfo;
+        LazyStat<GroupInfo> currentGroupInfo;
+        LazyStat<TestCaseInfo> currentTestCaseInfo;
 
-        NameAndLocation( std::string const& _name, SourceLineInfo const& _location )
-        :   name( _name ),
-            location( _location )
-        {}
+        std::vector<SectionInfo> m_sectionStack;
+        ReporterPreferences m_reporterPrefs;
     };
 
-    struct ITracker : SharedImpl<> {
-        virtual ~ITracker();
-
-        // static queries
-        virtual NameAndLocation const& nameAndLocation() const = 0;
-
-        // 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 ITracker& parent() = 0;
+    template<typename DerivedT>
+    struct CumulativeReporterBase : IStreamingReporter {
+        template<typename T, typename ChildNodeT>
+        struct Node {
+            explicit Node( T const& _value ) : value( _value ) {}
+            virtual ~Node() {}
 
-        // actions
-        virtual void close() = 0; // Successfully complete
-        virtual void fail() = 0;
-        virtual void markAsNeedingAnotherRun() = 0;
+            using ChildNodes = std::vector<std::shared_ptr<ChildNodeT>>;
+            T value;
+            ChildNodes children;
+        };
+        struct SectionNode {
+            explicit SectionNode(SectionStats const& _stats) : stats(_stats) {}
+            virtual ~SectionNode() = default;
 
-        virtual void addChild( Ptr<ITracker> const& child ) = 0;
-        virtual ITracker* findChild( NameAndLocation const& nameAndLocation ) = 0;
-        virtual void openChild() = 0;
+            bool operator == (SectionNode const& other) const {
+                return stats.sectionInfo.lineInfo == other.stats.sectionInfo.lineInfo;
+            }
+            bool operator == (std::shared_ptr<SectionNode> const& other) const {
+                return operator==(*other);
+            }
 
-        // Debug/ checking
-        virtual bool isSectionTracker() const = 0;
-        virtual bool isIndexTracker() const = 0;
-    };
+            SectionStats stats;
+            using ChildSections = std::vector<std::shared_ptr<SectionNode>>;
+            using Assertions = std::vector<AssertionStats>;
+            ChildSections childSections;
+            Assertions assertions;
+            std::string stdOut;
+            std::string stdErr;
+        };
 
-    class  TrackerContext {
+        struct BySectionInfo {
+            BySectionInfo( SectionInfo const& other ) : m_other( other ) {}
+            BySectionInfo( BySectionInfo const& other ) : m_other( other.m_other ) {}
+            bool operator() (std::shared_ptr<SectionNode> const& node) const {
+                return ((node->stats.sectionInfo.name == m_other.name) &&
+                        (node->stats.sectionInfo.lineInfo == m_other.lineInfo));
+            }
+            void operator=(BySectionInfo const&) = delete;
 
-        enum RunState {
-            NotStarted,
-            Executing,
-            CompletedCycle
+        private:
+            SectionInfo const& m_other;
         };
 
-        Ptr<ITracker> m_rootTracker;
-        ITracker* m_currentTracker;
-        RunState m_runState;
-
-    public:
+        using TestCaseNode = Node<TestCaseStats, SectionNode>;
+        using TestGroupNode = Node<TestGroupStats, TestCaseNode>;
+        using TestRunNode = Node<TestRunStats, TestGroupNode>;
 
-        static TrackerContext& instance() {
-            static TrackerContext s_instance;
-            return s_instance;
+        CumulativeReporterBase( ReporterConfig const& _config )
+        :   m_config( _config.fullConfig() ),
+            stream( _config.stream() )
+        {
+            m_reporterPrefs.shouldRedirectStdOut = false;
+            if( !DerivedT::getSupportedVerbosities().count( m_config->verbosity() ) )
+                CATCH_ERROR( "Verbosity level not supported by this reporter" );
         }
+        ~CumulativeReporterBase() override = default;
 
-        TrackerContext()
-        :   m_currentTracker( CATCH_NULL ),
-            m_runState( NotStarted )
-        {}
-
-        ITracker& startRun();
-
-        void endRun() {
-            m_rootTracker.reset();
-            m_currentTracker = CATCH_NULL;
-            m_runState = NotStarted;
+        ReporterPreferences getPreferences() const override {
+            return m_reporterPrefs;
         }
 
-        void startCycle() {
-            m_currentTracker = m_rootTracker.get();
-            m_runState = Executing;
-        }
-        void completeCycle() {
-            m_runState = CompletedCycle;
+        static std::set<Verbosity> getSupportedVerbosities() {
+            return { Verbosity::Normal };
         }
 
-        bool completedCycle() const {
-            return m_runState == CompletedCycle;
-        }
-        ITracker& currentTracker() {
-            return *m_currentTracker;
-        }
-        void setCurrentTracker( ITracker* tracker ) {
-            m_currentTracker = tracker;
-        }
-    };
+        void testRunStarting( TestRunInfo const& ) override {}
+        void testGroupStarting( GroupInfo const& ) override {}
 
-    class TrackerBase : public ITracker {
-    protected:
-        enum CycleState {
-            NotStarted,
-            Executing,
-            ExecutingChildren,
-            NeedsAnotherRun,
-            CompletedSuccessfully,
-            Failed
-        };
-        class TrackerHasName {
-            NameAndLocation m_nameAndLocation;
-        public:
-            TrackerHasName( NameAndLocation const& nameAndLocation ) : m_nameAndLocation( nameAndLocation ) {}
-            bool operator ()( Ptr<ITracker> const& tracker ) {
-                return
-                    tracker->nameAndLocation().name == m_nameAndLocation.name &&
-                    tracker->nameAndLocation().location == m_nameAndLocation.location;
+        void testCaseStarting( TestCaseInfo const& ) override {}
+
+        void sectionStarting( SectionInfo const& sectionInfo ) override {
+            SectionStats incompleteStats( sectionInfo, Counts(), 0, false );
+            std::shared_ptr<SectionNode> node;
+            if( m_sectionStack.empty() ) {
+                if( !m_rootSection )
+                    m_rootSection = std::make_shared<SectionNode>( incompleteStats );
+                node = m_rootSection;
             }
-        };
-        typedef std::vector<Ptr<ITracker> > Children;
-        NameAndLocation m_nameAndLocation;
-        TrackerContext& m_ctx;
-        ITracker* m_parent;
-        Children m_children;
-        CycleState m_runState;
-    public:
-        TrackerBase( NameAndLocation const& nameAndLocation, TrackerContext& ctx, ITracker* parent )
-        :   m_nameAndLocation( nameAndLocation ),
-            m_ctx( ctx ),
-            m_parent( parent ),
-            m_runState( NotStarted )
-        {}
-        virtual ~TrackerBase();
+            else {
+                SectionNode& parentNode = *m_sectionStack.back();
+                auto it =
+                    std::find_if(   parentNode.childSections.begin(),
+                                    parentNode.childSections.end(),
+                                    BySectionInfo( sectionInfo ) );
+                if( it == parentNode.childSections.end() ) {
+                    node = std::make_shared<SectionNode>( incompleteStats );
+                    parentNode.childSections.push_back( node );
+                }
+                else
+                    node = *it;
+            }
+            m_sectionStack.push_back( node );
+            m_deepestSection = std::move(node);
+        }
 
-        virtual NameAndLocation const& nameAndLocation() const CATCH_OVERRIDE {
-            return m_nameAndLocation;
+        void assertionStarting(AssertionInfo const&) override {}
+
+        bool assertionEnded(AssertionStats const& assertionStats) override {
+            assert(!m_sectionStack.empty());
+            // AssertionResult holds a pointer to a temporary DecomposedExpression,
+            // which getExpandedExpression() calls to build the expression string.
+            // Our section stack copy of the assertionResult will likely outlive the
+            // temporary, so it must be expanded or discarded now to avoid calling
+            // a destroyed object later.
+            prepareExpandedExpression(const_cast<AssertionResult&>( assertionStats.assertionResult ) );
+            SectionNode& sectionNode = *m_sectionStack.back();
+            sectionNode.assertions.push_back(assertionStats);
+            return true;
         }
-        virtual bool isComplete() const CATCH_OVERRIDE {
-            return m_runState == CompletedSuccessfully || m_runState == Failed;
+        void sectionEnded(SectionStats const& sectionStats) override {
+            assert(!m_sectionStack.empty());
+            SectionNode& node = *m_sectionStack.back();
+            node.stats = sectionStats;
+            m_sectionStack.pop_back();
         }
-        virtual bool isSuccessfullyCompleted() const CATCH_OVERRIDE {
-            return m_runState == CompletedSuccessfully;
+        void testCaseEnded(TestCaseStats const& testCaseStats) override {
+            auto node = std::make_shared<TestCaseNode>(testCaseStats);
+            assert(m_sectionStack.size() == 0);
+            node->children.push_back(m_rootSection);
+            m_testCases.push_back(node);
+            m_rootSection.reset();
+
+            assert(m_deepestSection);
+            m_deepestSection->stdOut = testCaseStats.stdOut;
+            m_deepestSection->stdErr = testCaseStats.stdErr;
         }
-        virtual bool isOpen() const CATCH_OVERRIDE {
-            return m_runState != NotStarted && !isComplete();
+        void testGroupEnded(TestGroupStats const& testGroupStats) override {
+            auto node = std::make_shared<TestGroupNode>(testGroupStats);
+            node->children.swap(m_testCases);
+            m_testGroups.push_back(node);
         }
-        virtual bool hasChildren() const CATCH_OVERRIDE {
-            return !m_children.empty();
+        void testRunEnded(TestRunStats const& testRunStats) override {
+            auto node = std::make_shared<TestRunNode>(testRunStats);
+            node->children.swap(m_testGroups);
+            m_testRuns.push_back(node);
+            testRunEndedCumulative();
         }
+        virtual void testRunEndedCumulative() = 0;
 
-        virtual void addChild( Ptr<ITracker> const& child ) CATCH_OVERRIDE {
-            m_children.push_back( child );
-        }
+        void skipTest(TestCaseInfo const&) override {}
 
-        virtual ITracker* findChild( NameAndLocation const& nameAndLocation ) CATCH_OVERRIDE {
-            Children::const_iterator it = std::find_if( m_children.begin(), m_children.end(), TrackerHasName( nameAndLocation ) );
-            return( it != m_children.end() )
-                ? it->get()
-                : CATCH_NULL;
-        }
-        virtual ITracker& parent() CATCH_OVERRIDE {
-            assert( m_parent ); // Should always be non-null except for root
-            return *m_parent;
-        }
+        IConfigPtr m_config;
+        std::ostream& stream;
+        std::vector<AssertionStats> m_assertions;
+        std::vector<std::vector<std::shared_ptr<SectionNode>>> m_sections;
+        std::vector<std::shared_ptr<TestCaseNode>> m_testCases;
+        std::vector<std::shared_ptr<TestGroupNode>> m_testGroups;
 
-        virtual void openChild() CATCH_OVERRIDE {
-            if( m_runState != ExecutingChildren ) {
-                m_runState = ExecutingChildren;
-                if( m_parent )
-                    m_parent->openChild();
-            }
-        }
+        std::vector<std::shared_ptr<TestRunNode>> m_testRuns;
 
-        virtual bool isSectionTracker() const CATCH_OVERRIDE { return false; }
-        virtual bool isIndexTracker() const CATCH_OVERRIDE { return false; }
+        std::shared_ptr<SectionNode> m_rootSection;
+        std::shared_ptr<SectionNode> m_deepestSection;
+        std::vector<std::shared_ptr<SectionNode>> m_sectionStack;
+        ReporterPreferences m_reporterPrefs;
+    };
 
-        void open() {
-            m_runState = Executing;
-            moveToThis();
-            if( m_parent )
-                m_parent->openChild();
+    template<char C>
+    char const* getLineOfChars() {
+        static char line[CATCH_CONFIG_CONSOLE_WIDTH] = {0};
+        if( !*line ) {
+            std::memset( line, C, CATCH_CONFIG_CONSOLE_WIDTH-1 );
+            line[CATCH_CONFIG_CONSOLE_WIDTH-1] = 0;
         }
+        return line;
+    }
 
-        virtual void close() CATCH_OVERRIDE {
+    struct TestEventListenerBase : StreamingReporterBase<TestEventListenerBase> {
+        TestEventListenerBase( ReporterConfig const& _config );
 
-            // Close any still open children (e.g. generators)
-            while( &m_ctx.currentTracker() != this )
-                m_ctx.currentTracker().close();
+        static std::set<Verbosity> getSupportedVerbosities();
 
-            switch( m_runState ) {
-                case NotStarted:
-                case CompletedSuccessfully:
-                case Failed:
-                    throw std::logic_error( "Illogical state" );
+        void assertionStarting(AssertionInfo const&) override;
+        bool assertionEnded(AssertionStats const&) override;
+    };
 
-                case NeedsAnotherRun:
-                    break;;
+} // end namespace Catch
 
-                case Executing:
-                    m_runState = CompletedSuccessfully;
-                    break;
-                case ExecutingChildren:
-                    if( m_children.empty() || m_children.back()->isComplete() )
-                        m_runState = CompletedSuccessfully;
-                    break;
+// end catch_reporter_bases.hpp
+// start catch_console_colour.h
 
-                default:
-                    throw std::logic_error( "Unexpected state" );
-            }
-            moveToParent();
-            m_ctx.completeCycle();
-        }
-        virtual void fail() CATCH_OVERRIDE {
-            m_runState = Failed;
-            if( m_parent )
-                m_parent->markAsNeedingAnotherRun();
-            moveToParent();
-            m_ctx.completeCycle();
-        }
-        virtual void markAsNeedingAnotherRun() CATCH_OVERRIDE {
-            m_runState = NeedsAnotherRun;
-        }
-    private:
-        void moveToParent() {
-            assert( m_parent );
-            m_ctx.setCurrentTracker( m_parent );
-        }
-        void moveToThis() {
-            m_ctx.setCurrentTracker( this );
-        }
-    };
+namespace Catch {
 
-    class SectionTracker : public TrackerBase {
-        std::vector<std::string> m_filters;
-    public:
-        SectionTracker( NameAndLocation const& nameAndLocation, TrackerContext& ctx, ITracker* parent )
-        :   TrackerBase( nameAndLocation, ctx, parent )
-        {
-            if( parent ) {
-                while( !parent->isSectionTracker() )
-                    parent = &parent->parent();
+    struct Colour {
+        enum Code {
+            None = 0,
 
-                SectionTracker& parentSection = static_cast<SectionTracker&>( *parent );
-                addNextFilters( parentSection.m_filters );
-            }
-        }
-        virtual ~SectionTracker();
+            White,
+            Red,
+            Green,
+            Blue,
+            Cyan,
+            Yellow,
+            Grey,
 
-        virtual bool isSectionTracker() const CATCH_OVERRIDE { return true; }
+            Bright = 0x10,
 
-        static SectionTracker& acquire( TrackerContext& ctx, NameAndLocation const& nameAndLocation ) {
-            SectionTracker* section = CATCH_NULL;
+            BrightRed = Bright | Red,
+            BrightGreen = Bright | Green,
+            LightGrey = Bright | Grey,
+            BrightWhite = Bright | White,
+            BrightYellow = Bright | Yellow,
 
-            ITracker& currentTracker = ctx.currentTracker();
-            if( ITracker* childTracker = currentTracker.findChild( nameAndLocation ) ) {
-                assert( childTracker );
-                assert( childTracker->isSectionTracker() );
-                section = static_cast<SectionTracker*>( childTracker );
-            }
-            else {
-                section = new SectionTracker( nameAndLocation, ctx, &currentTracker );
-                currentTracker.addChild( section );
-            }
-            if( !ctx.completedCycle() )
-                section->tryOpen();
-            return *section;
-        }
+            // By intention
+            FileName = LightGrey,
+            Warning = BrightYellow,
+            ResultError = BrightRed,
+            ResultSuccess = BrightGreen,
+            ResultExpectedFailure = Warning,
 
-        void tryOpen() {
-            if( !isComplete() && (m_filters.empty() || m_filters[0].empty() ||  m_filters[0] == m_nameAndLocation.name ) )
-                open();
-        }
+            Error = BrightRed,
+            Success = Green,
 
-        void 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.insert( m_filters.end(), filters.begin(), filters.end() );
-            }
-        }
-        void addNextFilters( std::vector<std::string> const& filters ) {
-            if( filters.size() > 1 )
-                m_filters.insert( m_filters.end(), ++filters.begin(), filters.end() );
-        }
+            OriginalExpression = Cyan,
+            ReconstructedExpression = BrightYellow,
+
+            SecondaryText = LightGrey,
+            Headers = White
+        };
+
+        // Use constructed object for RAII guard
+        Colour( Code _colourCode );
+        Colour( Colour&& other ) noexcept;
+        Colour& operator=( Colour&& other ) noexcept;
+        ~Colour();
+
+        // Use static method for one-shot changes
+        static void use( Code _colourCode );
+
+    private:
+        bool m_moved = false;
     };
 
-    class IndexTracker : public TrackerBase {
-        int m_size;
-        int m_index;
-    public:
-        IndexTracker( NameAndLocation const& nameAndLocation, TrackerContext& ctx, ITracker* parent, int size )
-        :   TrackerBase( nameAndLocation, ctx, parent ),
-            m_size( size ),
-            m_index( -1 )
-        {}
-        virtual ~IndexTracker();
+    std::ostream& operator << ( std::ostream& os, Colour const& );
 
-        virtual bool isIndexTracker() const CATCH_OVERRIDE { return true; }
+} // end namespace Catch
 
-        static IndexTracker& acquire( TrackerContext& ctx, NameAndLocation const& nameAndLocation, int size ) {
-            IndexTracker* tracker = CATCH_NULL;
+// end catch_console_colour.h
+// start catch_reporter_registrars.hpp
 
-            ITracker& currentTracker = ctx.currentTracker();
-            if( ITracker* childTracker = currentTracker.findChild( nameAndLocation ) ) {
-                assert( childTracker );
-                assert( childTracker->isIndexTracker() );
-                tracker = static_cast<IndexTracker*>( childTracker );
-            }
-            else {
-                tracker = new IndexTracker( nameAndLocation, ctx, &currentTracker, size );
-                currentTracker.addChild( tracker );
+
+namespace Catch {
+
+    template<typename T>
+    class ReporterRegistrar {
+
+        class ReporterFactory : public IReporterFactory {
+
+            IStreamingReporterPtr create( ReporterConfig const& config ) const override {
+                return std::unique_ptr<T>( new T( config ) );
             }
 
-            if( !ctx.completedCycle() && !tracker->isComplete() ) {
-                if( tracker->m_runState != ExecutingChildren && tracker->m_runState != NeedsAnotherRun )
-                    tracker->moveNext();
-                tracker->open();
+            std::string getDescription() const override {
+                return T::getDescription();
             }
+        };
+
+    public:
 
-            return *tracker;
+        explicit ReporterRegistrar( std::string const& name ) {
+            getMutableRegistryHub().registerReporter( name, std::make_shared<ReporterFactory>() );
         }
+    };
 
-        int index() const { return m_index; }
+    template<typename T>
+    class ListenerRegistrar {
 
-        void moveNext() {
-            m_index++;
-            m_children.clear();
-        }
+        class ListenerFactory : public IReporterFactory {
 
-        virtual void close() CATCH_OVERRIDE {
-            TrackerBase::close();
-            if( m_runState == CompletedSuccessfully && m_index < m_size-1 )
-                m_runState = Executing;
+            IStreamingReporterPtr create( ReporterConfig const& config ) const override {
+                return std::unique_ptr<T>( new T( config ) );
+            }
+            std::string getDescription() const override {
+                return std::string();
+            }
+        };
+
+    public:
+
+        ListenerRegistrar() {
+            getMutableRegistryHub().registerListener( std::make_shared<ListenerFactory>() );
         }
     };
+}
 
-    inline ITracker& TrackerContext::startRun() {
-        m_rootTracker = new SectionTracker( NameAndLocation( "{root}", CATCH_INTERNAL_LINEINFO ), *this, CATCH_NULL );
-        m_currentTracker = CATCH_NULL;
-        m_runState = Executing;
-        return *m_rootTracker;
-    }
+#if !defined(CATCH_CONFIG_DISABLE)
 
-} // namespace TestCaseTracking
+#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_STOP_WARNINGS_SUPPRESSION
 
-using TestCaseTracking::ITracker;
-using TestCaseTracking::TrackerContext;
-using TestCaseTracking::SectionTracker;
-using TestCaseTracking::IndexTracker;
+#define CATCH_REGISTER_LISTENER( listenerType ) \
+    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
 
-} // namespace Catch
+#define CATCH_REGISTER_REPORTER(name, reporterType)
+#define CATCH_REGISTER_LISTENER(listenerType)
 
-CATCH_INTERNAL_UNSUPPRESS_ETD_WARNINGS
+#endif // CATCH_CONFIG_DISABLE
 
-// #included from: catch_fatal_condition.hpp
-#define TWOBLUECUBES_CATCH_FATAL_CONDITION_H_INCLUDED
+// end catch_reporter_registrars.hpp
+// Allow users to base their work off existing reporters
+// start catch_reporter_compact.h
 
 namespace Catch {
 
-    // Report the error condition
-    inline void reportFatal( std::string const& message ) {
-        IContext& context = Catch::getCurrentContext();
-        IResultCapture* resultCapture = context.getResultCapture();
-        resultCapture->handleFatalErrorCondition( message );
-    }
+    struct CompactReporter : StreamingReporterBase<CompactReporter> {
 
-} // namespace Catch
+        using StreamingReporterBase::StreamingReporterBase;
 
-#if defined ( CATCH_PLATFORM_WINDOWS ) /////////////////////////////////////////
-// #included from: catch_windows_h_proxy.h
+        ~CompactReporter() override;
 
-#define TWOBLUECUBES_CATCH_WINDOWS_H_PROXY_H_INCLUDED
+        static std::string getDescription();
 
-#ifdef CATCH_DEFINES_NOMINMAX
-#  define NOMINMAX
-#endif
-#ifdef CATCH_DEFINES_WIN32_LEAN_AND_MEAN
-#  define WIN32_LEAN_AND_MEAN
-#endif
+        void noMatchingTestCases(std::string const& spec) override;
 
-#ifdef __AFXDLL
-#include <AfxWin.h>
-#else
-#include <windows.h>
-#endif
+        void assertionStarting(AssertionInfo const&) override;
 
-#ifdef CATCH_DEFINES_NOMINMAX
-#  undef NOMINMAX
-#endif
-#ifdef CATCH_DEFINES_WIN32_LEAN_AND_MEAN
-#  undef WIN32_LEAN_AND_MEAN
-#endif
+        bool assertionEnded(AssertionStats const& _assertionStats) override;
 
+        void sectionEnded(SectionStats const& _sectionStats) override;
 
-#  if !defined ( CATCH_CONFIG_WINDOWS_SEH )
+        void testRunEnded(TestRunStats const& _testRunStats) override;
 
-namespace Catch {
-    struct FatalConditionHandler {
-        void reset() {}
     };
-}
-
-#  else // CATCH_CONFIG_WINDOWS_SEH is defined
 
-namespace Catch {
+} // end namespace Catch
 
-    struct SignalDefs { DWORD id; const char* name; };
-    extern SignalDefs signalDefs[];
-    // 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.
-    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" },
-    };
+// end catch_reporter_compact.h
+// start catch_reporter_console.h
 
-    struct FatalConditionHandler {
+#if defined(_MSC_VER)
+#pragma warning(push)
+#pragma warning(disable:4061) // Not all labels are EXPLICITLY handled in switch
+                              // Note that 4062 (not all labels are handled
+                              // and default is missing) is enabled
+#endif
 
-        static LONG CALLBACK handleVectoredException(PEXCEPTION_POINTERS ExceptionInfo) {
-            for (int i = 0; i < sizeof(signalDefs) / sizeof(SignalDefs); ++i) {
-                if (ExceptionInfo->ExceptionRecord->ExceptionCode == signalDefs[i].id) {
-                    reportFatal(signalDefs[i].name);
-                }
-            }
-            // If its not an exception we care about, pass it along.
-            // This stops us from eating debugger breaks etc.
-            return EXCEPTION_CONTINUE_SEARCH;
-        }
+namespace Catch {
+    // Fwd decls
+    struct SummaryColumn;
+    class TablePrinter;
 
-        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 = CATCH_NULL;
-            // Register as first handler in current chain
-            exceptionHandlerHandle = AddVectoredExceptionHandler(1, handleVectoredException);
-            // Pass in guarantee size to be filled
-            SetThreadStackGuarantee(&guaranteeSize);
-        }
+    struct ConsoleReporter : StreamingReporterBase<ConsoleReporter> {
+        std::unique_ptr<TablePrinter> m_tablePrinter;
 
-        static void reset() {
-            if (isSet) {
-                // Unregister handler and restore the old guarantee
-                RemoveVectoredExceptionHandler(exceptionHandlerHandle);
-                SetThreadStackGuarantee(&guaranteeSize);
-                exceptionHandlerHandle = CATCH_NULL;
-                isSet = false;
-            }
-        }
+        ConsoleReporter(ReporterConfig const& config);
+        ~ConsoleReporter() override;
+        static std::string getDescription();
 
-        ~FatalConditionHandler() {
-            reset();
-        }
-    private:
-        static bool isSet;
-        static ULONG guaranteeSize;
-        static PVOID exceptionHandlerHandle;
-    };
+        void noMatchingTestCases(std::string const& spec) override;
 
-    bool FatalConditionHandler::isSet = false;
-    ULONG FatalConditionHandler::guaranteeSize = 0;
-    PVOID FatalConditionHandler::exceptionHandlerHandle = CATCH_NULL;
+        void reportInvalidArguments(std::string const&arg) override;
 
-} // namespace Catch
+        void assertionStarting(AssertionInfo const&) override;
 
-#  endif // CATCH_CONFIG_WINDOWS_SEH
+        bool assertionEnded(AssertionStats const& _assertionStats) override;
 
-#else // Not Windows - assumed to be POSIX compatible //////////////////////////
+        void sectionStarting(SectionInfo const& _sectionInfo) override;
+        void sectionEnded(SectionStats const& _sectionStats) override;
 
-#  if !defined(CATCH_CONFIG_POSIX_SIGNALS)
+#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 benchmarkFailed(std::string const& error) override;
+#endif // CATCH_CONFIG_ENABLE_BENCHMARKING
 
-namespace Catch {
-    struct FatalConditionHandler {
-        void reset() {}
-    };
-}
+        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:
 
-#  else // CATCH_CONFIG_POSIX_SIGNALS is defined
+        void lazyPrint();
 
-#include <signal.h>
+        void lazyPrintWithoutClosingBenchmarkTable();
+        void lazyPrintRunInfo();
+        void lazyPrintGroupInfo();
+        void printTestCaseAndSectionHeader();
 
-namespace Catch {
+        void printClosedHeader(std::string const& _name);
+        void printOpenHeader(std::string const& _name);
 
-    struct SignalDefs {
-        int id;
-        const char* name;
-    };
-    extern SignalDefs signalDefs[];
-    SignalDefs signalDefs[] = {
-            { SIGINT,  "SIGINT - Terminal interrupt signal" },
-            { SIGILL,  "SIGILL - Illegal instruction signal" },
-            { SIGFPE,  "SIGFPE - Floating point error signal" },
-            { SIGSEGV, "SIGSEGV - Segmentation violation signal" },
-            { SIGTERM, "SIGTERM - Termination request signal" },
-            { SIGABRT, "SIGABRT - Abort (abnormal termination) signal" }
-    };
-
-    struct FatalConditionHandler {
-
-        static bool isSet;
-        static struct sigaction oldSigActions [sizeof(signalDefs)/sizeof(SignalDefs)];
-        static stack_t oldSigStack;
-        static char altStackMem[SIGSTKSZ];
-
-        static void handleSignal( int sig ) {
-            std::string name = "<unknown signal>";
-            for (std::size_t i = 0; i < sizeof(signalDefs) / sizeof(SignalDefs); ++i) {
-                SignalDefs &def = signalDefs[i];
-                if (sig == def.id) {
-                    name = def.name;
-                    break;
-                }
-            }
-            reset();
-            reportFatal(name);
-            raise( sig );
-        }
+        // if string has a : in first line will set indent to follow it on
+        // subsequent lines
+        void printHeaderString(std::string const& _string, std::size_t indent = 0);
 
-        FatalConditionHandler() {
-            isSet = true;
-            stack_t sigStack;
-            sigStack.ss_sp = altStackMem;
-            sigStack.ss_size = SIGSTKSZ;
-            sigStack.ss_flags = 0;
-            sigaltstack(&sigStack, &oldSigStack);
-            struct sigaction sa = { 0 };
+        void printTotals(Totals const& totals);
+        void printSummaryRow(std::string const& label, std::vector<SummaryColumn> const& cols, std::size_t row);
 
-            sa.sa_handler = handleSignal;
-            sa.sa_flags = SA_ONSTACK;
-            for (std::size_t i = 0; i < sizeof(signalDefs)/sizeof(SignalDefs); ++i) {
-                sigaction(signalDefs[i].id, &sa, &oldSigActions[i]);
-            }
-        }
+        void printTotalsDivider(Totals const& totals);
+        void printSummaryDivider();
+        void printTestFilters();
 
-        ~FatalConditionHandler() {
-            reset();
-        }
-        static void 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], CATCH_NULL);
-                }
-                // Return the old stack
-                sigaltstack(&oldSigStack, CATCH_NULL);
-                isSet = false;
-            }
-        }
+    private:
+        bool m_headerPrinted = false;
     };
 
-    bool FatalConditionHandler::isSet = false;
-    struct sigaction FatalConditionHandler::oldSigActions[sizeof(signalDefs)/sizeof(SignalDefs)] = {};
-    stack_t FatalConditionHandler::oldSigStack = {};
-    char FatalConditionHandler::altStackMem[SIGSTKSZ] = {};
+} // end namespace Catch
 
-} // namespace Catch
+#if defined(_MSC_VER)
+#pragma warning(pop)
+#endif
 
-#  endif // CATCH_CONFIG_POSIX_SIGNALS
+// end catch_reporter_console.h
+// start catch_reporter_junit.h
 
-#endif // not Windows
+// start catch_xmlwriter.h
 
-#include <cassert>
-#include <set>
-#include <string>
+#include <vector>
 
 namespace Catch {
+    enum class XmlFormatting {
+        None = 0x00,
+        Indent = 0x01,
+        Newline = 0x02,
+    };
 
-    class StreamRedirect {
+    XmlFormatting operator | (XmlFormatting lhs, XmlFormatting rhs);
+    XmlFormatting operator & (XmlFormatting lhs, XmlFormatting rhs);
 
+    class XmlEncode {
     public:
-        StreamRedirect( std::ostream& stream, std::string& targetString )
-        :   m_stream( stream ),
-            m_prevBuf( stream.rdbuf() ),
-            m_targetString( targetString )
-        {
-            stream.rdbuf( m_oss.rdbuf() );
-        }
+        enum ForWhat { ForTextNodes, ForAttributes };
 
-        ~StreamRedirect() {
-            m_targetString += m_oss.str();
-            m_stream.rdbuf( m_prevBuf );
-        }
+        XmlEncode( std::string const& str, ForWhat forWhat = ForTextNodes );
 
-    private:
-        std::ostream& m_stream;
-        std::streambuf* m_prevBuf;
-        std::ostringstream m_oss;
-        std::string& m_targetString;
-    };
+        void encodeTo( std::ostream& os ) const;
+
+        friend std::ostream& operator << ( std::ostream& os, XmlEncode const& xmlEncode );
 
-    // StdErr has two constituent streams in C++, std::cerr and std::clog
-    // This means that we need to redirect 2 streams into 1 to keep proper
-    // order of writes and cannot use StreamRedirect on its own
-    class StdErrRedirect {
-    public:
-        StdErrRedirect(std::string& targetString)
-        :m_cerrBuf( cerr().rdbuf() ), m_clogBuf(clog().rdbuf()),
-        m_targetString(targetString){
-            cerr().rdbuf(m_oss.rdbuf());
-            clog().rdbuf(m_oss.rdbuf());
-        }
-        ~StdErrRedirect() {
-            m_targetString += m_oss.str();
-            cerr().rdbuf(m_cerrBuf);
-            clog().rdbuf(m_clogBuf);
-        }
     private:
-        std::streambuf* m_cerrBuf;
-        std::streambuf* m_clogBuf;
-        std::ostringstream m_oss;
-        std::string& m_targetString;
+        std::string m_str;
+        ForWhat m_forWhat;
     };
 
-    ///////////////////////////////////////////////////////////////////////////
-
-    class RunContext : public IResultCapture, public IRunner {
-
-        RunContext( RunContext const& );
-        void operator =( RunContext const& );
-
+    class XmlWriter {
     public:
 
-        explicit RunContext( Ptr<IConfig const> const& _config, Ptr<IStreamingReporter> const& reporter )
-        :   m_runInfo( _config->name() ),
-            m_context( getCurrentMutableContext() ),
-            m_activeTestCase( CATCH_NULL ),
-            m_config( _config ),
-            m_reporter( reporter ),
-            m_shouldReportUnexpected ( true )
-        {
-            m_context.setRunner( this );
-            m_context.setConfig( m_config );
-            m_context.setResultCapture( this );
-            m_reporter->testRunStarting( m_runInfo );
-        }
+        class ScopedElement {
+        public:
+            ScopedElement( XmlWriter* writer, XmlFormatting fmt );
 
-        virtual ~RunContext() {
-            m_reporter->testRunEnded( TestRunStats( m_runInfo, m_totals, aborting() ) );
-        }
+            ScopedElement( ScopedElement&& other ) noexcept;
+            ScopedElement& operator=( ScopedElement&& other ) noexcept;
 
-        void testGroupStarting( std::string const& testSpec, std::size_t groupIndex, std::size_t groupsCount ) {
-            m_reporter->testGroupStarting( GroupInfo( testSpec, groupIndex, groupsCount ) );
-        }
-        void testGroupEnded( std::string const& testSpec, Totals const& totals, std::size_t groupIndex, std::size_t groupsCount ) {
-            m_reporter->testGroupEnded( TestGroupStats( GroupInfo( testSpec, groupIndex, groupsCount ), totals, aborting() ) );
-        }
+            ~ScopedElement();
 
-        Totals runTest( TestCase const& testCase ) {
-            Totals prevTotals = m_totals;
+            ScopedElement& writeText( std::string const& text, XmlFormatting fmt = XmlFormatting::Newline | XmlFormatting::Indent );
+
+            template<typename T>
+            ScopedElement& writeAttribute( std::string const& name, T const& attribute ) {
+                m_writer->writeAttribute( name, attribute );
+                return *this;
+            }
 
-            std::string redirectedCout;
-            std::string redirectedCerr;
+        private:
+            mutable XmlWriter* m_writer = nullptr;
+            XmlFormatting m_fmt;
+        };
 
-            TestCaseInfo testInfo = testCase.getTestCaseInfo();
+        XmlWriter( std::ostream& os = Catch::cout() );
+        ~XmlWriter();
 
-            m_reporter->testCaseStarting( testInfo );
+        XmlWriter( XmlWriter const& ) = delete;
+        XmlWriter& operator=( XmlWriter const& ) = delete;
 
-            m_activeTestCase = &testCase;
+        XmlWriter& startElement( std::string const& name, XmlFormatting fmt = XmlFormatting::Newline | XmlFormatting::Indent);
 
-            do {
-                ITracker& rootTracker = m_trackerContext.startRun();
-                assert( rootTracker.isSectionTracker() );
-                static_cast<SectionTracker&>( rootTracker ).addInitialFilters( m_config->getSectionsToRun() );
-                do {
-                    m_trackerContext.startCycle();
-                    m_testCaseTracker = &SectionTracker::acquire( m_trackerContext, TestCaseTracking::NameAndLocation( testInfo.name, testInfo.lineInfo ) );
-                    runCurrentTest( redirectedCout, redirectedCerr );
-                }
-                while( !m_testCaseTracker->isSuccessfullyCompleted() && !aborting() );
-            }
-            // !TBD: deprecated - this will be replaced by indexed trackers
-            while( getCurrentContext().advanceGeneratorsForCurrentTest() && !aborting() );
+        ScopedElement scopedElement( std::string const& name, XmlFormatting fmt = XmlFormatting::Newline | XmlFormatting::Indent);
 
-            Totals deltaTotals = m_totals.delta( prevTotals );
-            if( testInfo.expectedToFail() && deltaTotals.testCases.passed > 0 ) {
-                deltaTotals.assertions.failed++;
-                deltaTotals.testCases.passed--;
-                deltaTotals.testCases.failed++;
-            }
-            m_totals.testCases += deltaTotals.testCases;
-            m_reporter->testCaseEnded( TestCaseStats(   testInfo,
-                                                        deltaTotals,
-                                                        redirectedCout,
-                                                        redirectedCerr,
-                                                        aborting() ) );
+        XmlWriter& endElement(XmlFormatting fmt = XmlFormatting::Newline | XmlFormatting::Indent);
 
-            m_activeTestCase = CATCH_NULL;
-            m_testCaseTracker = CATCH_NULL;
+        XmlWriter& writeAttribute( std::string const& name, std::string const& attribute );
 
-            return deltaTotals;
-        }
+        XmlWriter& writeAttribute( std::string const& name, bool attribute );
 
-        Ptr<IConfig const> config() const {
-            return m_config;
+        template<typename T>
+        XmlWriter& writeAttribute( std::string const& name, T const& attribute ) {
+            ReusableStringStream rss;
+            rss << attribute;
+            return writeAttribute( name, rss.str() );
         }
 
-    private: // IResultCapture
+        XmlWriter& writeText( std::string const& text, XmlFormatting fmt = XmlFormatting::Newline | XmlFormatting::Indent);
 
-        virtual void assertionEnded( AssertionResult const& result ) {
-            if( result.getResultType() == ResultWas::Ok ) {
-                m_totals.assertions.passed++;
-            }
-            else if( !result.isOk() ) {
-                if( m_activeTestCase->getTestCaseInfo().okToFail() )
-                    m_totals.assertions.failedButOk++;
-                else
-                    m_totals.assertions.failed++;
-            }
+        XmlWriter& writeComment(std::string const& text, XmlFormatting fmt = XmlFormatting::Newline | XmlFormatting::Indent);
 
-            // We have no use for the return value (whether messages should be cleared), because messages were made scoped
-            // and should be let to clear themselves out.
-            static_cast<void>(m_reporter->assertionEnded(AssertionStats(result, m_messages, m_totals)));
+        void writeStylesheetRef( std::string const& url );
 
-            // Reset working state
-            m_lastAssertionInfo = AssertionInfo( "", m_lastAssertionInfo.lineInfo, "{Unknown expression after the reported line}" , m_lastAssertionInfo.resultDisposition );
-            m_lastResult = result;
-        }
+        XmlWriter& writeBlankLine();
 
-        virtual bool lastAssertionPassed()
-        {
-            return m_totals.assertions.passed == (m_prevPassed + 1);
-        }
+        void ensureTagClosed();
 
-        virtual void assertionPassed()
-        {
-            m_totals.assertions.passed++;
-            m_lastAssertionInfo.capturedExpression = "{Unknown expression after the reported line}";
-            m_lastAssertionInfo.macroName = "";
-        }
+    private:
 
-        virtual void assertionRun()
-        {
-            m_prevPassed = m_totals.assertions.passed;
-        }
+        void applyFormatting(XmlFormatting fmt);
 
-        virtual bool sectionStarted (
-            SectionInfo const& sectionInfo,
-            Counts& assertions
-        )
-        {
-            ITracker& sectionTracker = SectionTracker::acquire( m_trackerContext, TestCaseTracking::NameAndLocation( sectionInfo.name, sectionInfo.lineInfo ) );
-            if( !sectionTracker.isOpen() )
-                return false;
-            m_activeSections.push_back( &sectionTracker );
+        void writeDeclaration();
 
-            m_lastAssertionInfo.lineInfo = sectionInfo.lineInfo;
+        void newlineIfNecessary();
 
-            m_reporter->sectionStarting( sectionInfo );
+        bool m_tagIsOpen = false;
+        bool m_needsNewline = false;
+        std::vector<std::string> m_tags;
+        std::string m_indent;
+        std::ostream& m_os;
+    };
 
-            assertions = m_totals.assertions;
+}
 
-            return true;
-        }
-        bool testForMissingAssertions( Counts& assertions ) {
-            if( assertions.total() != 0 )
-                return false;
-            if( !m_config->warnAboutMissingAssertions() )
-                return false;
-            if( m_trackerContext.currentTracker().hasChildren() )
-                return false;
-            m_totals.assertions.failed++;
-            assertions.failed++;
-            return true;
-        }
+// end catch_xmlwriter.h
+namespace Catch {
 
-        virtual void sectionEnded( SectionEndInfo const& endInfo ) {
-            Counts assertions = m_totals.assertions - endInfo.prevAssertions;
-            bool missingAssertions = testForMissingAssertions( assertions );
+    class JunitReporter : public CumulativeReporterBase<JunitReporter> {
+    public:
+        JunitReporter(ReporterConfig const& _config);
 
-            if( !m_activeSections.empty() ) {
-                m_activeSections.back()->close();
-                m_activeSections.pop_back();
-            }
+        ~JunitReporter() override;
 
-            m_reporter->sectionEnded( SectionStats( endInfo.sectionInfo, assertions, endInfo.durationInSeconds, missingAssertions ) );
-            m_messages.clear();
-        }
+        static std::string getDescription();
 
-        virtual void sectionEndedEarly( SectionEndInfo const& endInfo ) {
-            if( m_unfinishedSections.empty() )
-                m_activeSections.back()->fail();
-            else
-                m_activeSections.back()->close();
-            m_activeSections.pop_back();
+        void noMatchingTestCases(std::string const& /*spec*/) override;
 
-            m_unfinishedSections.push_back( endInfo );
-        }
+        void testRunStarting(TestRunInfo const& runInfo) override;
 
-        virtual void pushScopedMessage( MessageInfo const& message ) {
-            m_messages.push_back( message );
-        }
+        void testGroupStarting(GroupInfo const& groupInfo) override;
 
-        virtual void popScopedMessage( MessageInfo const& message ) {
-            m_messages.erase( std::remove( m_messages.begin(), m_messages.end(), message ), m_messages.end() );
-        }
+        void testCaseStarting(TestCaseInfo const& testCaseInfo) override;
+        bool assertionEnded(AssertionStats const& assertionStats) override;
 
-        virtual std::string getCurrentTestName() const {
-            return m_activeTestCase
-                ? m_activeTestCase->getTestCaseInfo().name
-                : std::string();
-        }
+        void testCaseEnded(TestCaseStats const& testCaseStats) override;
 
-        virtual const AssertionResult* getLastResult() const {
-            return &m_lastResult;
-        }
+        void testGroupEnded(TestGroupStats const& testGroupStats) override;
 
-        virtual void exceptionEarlyReported() {
-            m_shouldReportUnexpected = false;
-        }
+        void testRunEndedCumulative() override;
 
-        virtual void handleFatalErrorCondition( std::string const& message ) {
-            // Don't rebuild the result -- the stringification itself can cause more fatal errors
-            // Instead, fake a result data.
-            AssertionResultData tempResult;
-            tempResult.resultType = ResultWas::FatalErrorCondition;
-            tempResult.message = message;
-            AssertionResult result(m_lastAssertionInfo, tempResult);
+        void writeGroup(TestGroupNode const& groupNode, double suiteTime);
 
-            getResultCapture().assertionEnded(result);
+        void writeTestCase(TestCaseNode const& testCaseNode);
 
-            handleUnfinishedSections();
+        void writeSection( std::string const& className,
+                           std::string const& rootName,
+                           SectionNode const& sectionNode,
+                           bool testOkToFail );
 
-            // Recreate section for test case (as we will lose the one that was in scope)
-            TestCaseInfo const& testCaseInfo = m_activeTestCase->getTestCaseInfo();
-            SectionInfo testCaseSection( testCaseInfo.lineInfo, testCaseInfo.name, testCaseInfo.description );
+        void writeAssertions(SectionNode const& sectionNode);
+        void writeAssertion(AssertionStats const& stats);
 
-            Counts assertions;
-            assertions.failed = 1;
-            SectionStats testCaseSectionStats( testCaseSection, assertions, 0, false );
-            m_reporter->sectionEnded( testCaseSectionStats );
+        XmlWriter xml;
+        Timer suiteTimer;
+        std::string stdOutForSuite;
+        std::string stdErrForSuite;
+        unsigned int unexpectedExceptions = 0;
+        bool m_okToFail = false;
+    };
 
-            TestCaseInfo testInfo = m_activeTestCase->getTestCaseInfo();
+} // end namespace Catch
 
-            Totals deltaTotals;
-            deltaTotals.testCases.failed = 1;
-            deltaTotals.assertions.failed = 1;
-            m_reporter->testCaseEnded( TestCaseStats(   testInfo,
-                                                        deltaTotals,
-                                                        std::string(),
-                                                        std::string(),
-                                                        false ) );
-            m_totals.testCases.failed++;
-            testGroupEnded( std::string(), m_totals, 1, 1 );
-            m_reporter->testRunEnded( TestRunStats( m_runInfo, m_totals, false ) );
-        }
+// end catch_reporter_junit.h
+// start catch_reporter_xml.h
 
+namespace Catch {
+    class XmlReporter : public StreamingReporterBase<XmlReporter> {
     public:
-        // !TBD We need to do this another way!
-        bool aborting() const {
-            return m_totals.assertions.failed == static_cast<std::size_t>( m_config->abortAfter() );
-        }
+        XmlReporter(ReporterConfig const& _config);
 
-    private:
-
-        void runCurrentTest( std::string& redirectedCout, std::string& redirectedCerr ) {
-            TestCaseInfo const& testCaseInfo = m_activeTestCase->getTestCaseInfo();
-            SectionInfo testCaseSection( testCaseInfo.lineInfo, testCaseInfo.name, testCaseInfo.description );
-            m_reporter->sectionStarting( testCaseSection );
-            Counts prevAssertions = m_totals.assertions;
-            double duration = 0;
-            m_shouldReportUnexpected = true;
-            try {
-                m_lastAssertionInfo = AssertionInfo( "TEST_CASE", testCaseInfo.lineInfo, "", ResultDisposition::Normal );
-
-                seedRng( *m_config );
-
-                Timer timer;
-                timer.start();
-                if( m_reporter->getPreferences().shouldRedirectStdOut ) {
-                    StreamRedirect coutRedir( Catch::cout(), redirectedCout );
-                    StdErrRedirect errRedir( redirectedCerr );
-                    invokeActiveTestCase();
-                }
-                else {
-                    invokeActiveTestCase();
-                }
-                duration = timer.getElapsedSeconds();
-            }
-            catch( TestFailureException& ) {
-                // This just means the test was aborted due to failure
-            }
-            catch(...) {
-                // Under CATCH_CONFIG_FAST_COMPILE, unexpected exceptions under REQUIRE assertions
-                // are reported without translation at the point of origin.
-                if (m_shouldReportUnexpected) {
-                    makeUnexpectedResultBuilder().useActiveException();
-                }
-            }
-            m_testCaseTracker->close();
-            handleUnfinishedSections();
-            m_messages.clear();
+        ~XmlReporter() override;
 
-            Counts assertions = m_totals.assertions - prevAssertions;
-            bool missingAssertions = testForMissingAssertions( assertions );
+        static std::string getDescription();
 
-            SectionStats testCaseSectionStats( testCaseSection, assertions, duration, missingAssertions );
-            m_reporter->sectionEnded( testCaseSectionStats );
-        }
+        virtual std::string getStylesheetRef() const;
 
-        void invokeActiveTestCase() {
-            FatalConditionHandler fatalConditionHandler; // Handle signals
-            m_activeTestCase->invoke();
-            fatalConditionHandler.reset();
-        }
+        void writeSourceInfo(SourceLineInfo const& sourceInfo);
 
-    private:
+    public: // StreamingReporterBase
 
-        ResultBuilder makeUnexpectedResultBuilder() const {
-            return ResultBuilder(   m_lastAssertionInfo.macroName,
-                                    m_lastAssertionInfo.lineInfo,
-                                    m_lastAssertionInfo.capturedExpression,
-                                    m_lastAssertionInfo.resultDisposition );
-        }
+        void noMatchingTestCases(std::string const& s) override;
 
-        void handleUnfinishedSections() {
-            // If sections ended prematurely due to an exception we stored their
-            // infos here so we can tear them down outside the unwind process.
-            for( std::vector<SectionEndInfo>::const_reverse_iterator it = m_unfinishedSections.rbegin(),
-                        itEnd = m_unfinishedSections.rend();
-                    it != itEnd;
-                    ++it )
-                sectionEnded( *it );
-            m_unfinishedSections.clear();
-        }
+        void testRunStarting(TestRunInfo const& testInfo) override;
 
-        TestRunInfo m_runInfo;
-        IMutableContext& m_context;
-        TestCase const* m_activeTestCase;
-        ITracker* m_testCaseTracker;
-        ITracker* m_currentSectionTracker;
-        AssertionResult m_lastResult;
+        void testGroupStarting(GroupInfo const& groupInfo) override;
 
-        Ptr<IConfig const> m_config;
-        Totals m_totals;
-        Ptr<IStreamingReporter> m_reporter;
-        std::vector<MessageInfo> m_messages;
-        AssertionInfo m_lastAssertionInfo;
-        std::vector<SectionEndInfo> m_unfinishedSections;
-        std::vector<ITracker*> m_activeSections;
-        TrackerContext m_trackerContext;
-        size_t m_prevPassed;
-        bool m_shouldReportUnexpected;
-    };
+        void testCaseStarting(TestCaseInfo const& testInfo) override;
 
-    IResultCapture& getResultCapture() {
-        if( IResultCapture* capture = getCurrentContext().getResultCapture() )
-            return *capture;
-        else
-            throw std::logic_error( "No result capture instance" );
-    }
+        void sectionStarting(SectionInfo const& sectionInfo) override;
 
-} // end namespace Catch
+        void assertionStarting(AssertionInfo const&) override;
 
-// #included from: internal/catch_version.h
-#define TWOBLUECUBES_CATCH_VERSION_H_INCLUDED
+        bool assertionEnded(AssertionStats const& assertionStats) override;
 
-namespace Catch {
+        void sectionEnded(SectionStats const& sectionStats) override;
 
-    // Versioning information
-    struct Version {
-        Version(    unsigned int _majorVersion,
-                    unsigned int _minorVersion,
-                    unsigned int _patchNumber,
-                    char const * const _branchName,
-                    unsigned int _buildNumber );
+        void testCaseEnded(TestCaseStats const& testCaseStats) override;
 
-        unsigned int const majorVersion;
-        unsigned int const minorVersion;
-        unsigned int const patchNumber;
+        void testGroupEnded(TestGroupStats const& testGroupStats) override;
 
-        // buildNumber is only used if branchName is not null
-        char const * const branchName;
-        unsigned int const buildNumber;
+        void testRunEnded(TestRunStats const& testRunStats) override;
 
-        friend std::ostream& operator << ( std::ostream& os, Version const& version );
+#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:
-        void operator=( Version const& );
+        Timer m_testCaseTimer;
+        XmlWriter m_xml;
+        int m_sectionDepth = 0;
     };
 
-    inline Version libraryVersion();
-}
+} // end namespace Catch
 
-#include <fstream>
-#include <stdlib.h>
-#include <limits>
+// end catch_reporter_xml.h
 
-namespace Catch {
+// end catch_external_interfaces.h
+#endif
 
-    Ptr<IStreamingReporter> createReporter( std::string const& reporterName, Ptr<Config> const& config ) {
-        Ptr<IStreamingReporter> reporter = getRegistryHub().getReporterRegistry().create( reporterName, config.get() );
-        if( !reporter ) {
-            std::ostringstream oss;
-            oss << "No reporter registered with name: '" << reporterName << "'";
-            throw std::domain_error( oss.str() );
-        }
-        return reporter;
-    }
+#if defined(CATCH_CONFIG_ENABLE_BENCHMARKING)
+// start catch_benchmarking_all.hpp
 
-#if !defined(CATCH_CONFIG_DEFAULT_REPORTER)
-#define CATCH_CONFIG_DEFAULT_REPORTER "console"
-#endif
+// 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.
 
-    Ptr<IStreamingReporter> makeReporter( Ptr<Config> const& config ) {
-        std::vector<std::string> reporters = config->getReporterNames();
-        if( reporters.empty() )
-            reporters.push_back( CATCH_CONFIG_DEFAULT_REPORTER );
+// start catch_benchmark.hpp
 
-        Ptr<IStreamingReporter> reporter;
-        for( std::vector<std::string>::const_iterator it = reporters.begin(), itEnd = reporters.end();
-                it != itEnd;
-                ++it )
-            reporter = addReporter( reporter, createReporter( *it, config ) );
-        return reporter;
-    }
-    Ptr<IStreamingReporter> addListeners( Ptr<IConfig const> const& config, Ptr<IStreamingReporter> reporters ) {
-        IReporterRegistry::Listeners listeners = getRegistryHub().getReporterRegistry().getListeners();
-        for( IReporterRegistry::Listeners::const_iterator it = listeners.begin(), itEnd = listeners.end();
-                it != itEnd;
-                ++it )
-            reporters = addReporter(reporters, (*it)->create( ReporterConfig( config ) ) );
-        return reporters;
-    }
+ // Benchmark
 
-    Totals runTests( Ptr<Config> const& config ) {
+// start catch_chronometer.hpp
 
-        Ptr<IConfig const> iconfig = config.get();
+// User-facing chronometer
 
-        Ptr<IStreamingReporter> reporter = makeReporter( config );
-        reporter = addListeners( iconfig, reporter );
 
-        RunContext context( iconfig, reporter );
+// start catch_clock.hpp
 
-        Totals totals;
+// Clocks
 
-        context.testGroupStarting( config->name(), 1, 1 );
 
-        TestSpec testSpec = config->testSpec();
-        if( !testSpec.hasFilters() )
-            testSpec = TestSpecParser( ITagAliasRegistry::get() ).parse( "~[.]" ).testSpec(); // All not hidden tests
+#include <chrono>
+#include <ratio>
 
-        std::vector<TestCase> const& allTestCases = getAllTestCasesSorted( *iconfig );
-        for( std::vector<TestCase>::const_iterator it = allTestCases.begin(), itEnd = allTestCases.end();
-                it != itEnd;
-                ++it ) {
-            if( !context.aborting() && matchTest( *it, testSpec, *iconfig ) )
-                totals += context.runTest( *it );
-            else
-                reporter->skipTest( *it );
-        }
+namespace Catch {
+    namespace Benchmark {
+        template <typename Clock>
+        using ClockDuration = typename Clock::duration;
+        template <typename Clock>
+        using FloatDuration = std::chrono::duration<double, typename Clock::period>;
 
-        context.testGroupEnded( iconfig->name(), totals, 1, 1 );
-        return totals;
-    }
+        template <typename Clock>
+        using TimePoint = typename Clock::time_point;
 
-    void applyFilenamesAsTags( IConfig const& config ) {
-        std::vector<TestCase> const& tests = getAllTestCasesSorted( config );
-        for(std::size_t i = 0; i < tests.size(); ++i ) {
-            TestCase& test = const_cast<TestCase&>( tests[i] );
-            std::set<std::string> tags = test.tags;
+        using default_clock = std::chrono::steady_clock;
 
-            std::string filename = test.lineInfo.file;
-            std::string::size_type lastSlash = filename.find_last_of( "\\/" );
-            if( lastSlash != std::string::npos )
-                filename = filename.substr( lastSlash+1 );
+        template <typename Clock>
+        struct now {
+            TimePoint<Clock> operator()() const {
+                return Clock::now();
+            }
+        };
 
-            std::string::size_type lastDot = filename.find_last_of( '.' );
-            if( lastDot != std::string::npos )
-                filename = filename.substr( 0, lastDot );
+        using fp_seconds = std::chrono::duration<double, std::ratio<1>>;
+    } // namespace Benchmark
+} // namespace Catch
 
-            tags.insert( '#' + filename );
-            setTags( test, tags );
-        }
-    }
+// end catch_clock.hpp
+// start catch_optimizer.hpp
 
-    class Session : NonCopyable {
-        static bool alreadyInstantiated;
+ // Hinting the optimizer
 
-    public:
 
-        struct OnUnusedOptions { enum DoWhat { Ignore, Fail }; };
+#if defined(_MSC_VER)
+#   include <atomic> // atomic_thread_fence
+#endif
 
-        Session()
-        : m_cli( makeCommandLineParser() ) {
-            if( alreadyInstantiated ) {
-                std::string msg = "Only one instance of Catch::Session can ever be used";
-                Catch::cerr() << msg << std::endl;
-                throw std::logic_error( msg );
-            }
-            alreadyInstantiated = true;
+namespace Catch {
+    namespace Benchmark {
+#if defined(__GNUC__) || defined(__clang__)
+        template <typename T>
+        inline void keep_memory(T* p) {
+            asm volatile("" : : "g"(p) : "memory");
         }
-        ~Session() {
-            Catch::cleanUp();
+        inline void keep_memory() {
+            asm volatile("" : : : "memory");
         }
 
-        void showHelp( std::string const& processName ) {
-            Catch::cout() << "\nCatch v" << libraryVersion() << "\n";
+        namespace Detail {
+            inline void optimizer_barrier() { keep_memory(); }
+        } // namespace Detail
+#elif defined(_MSC_VER)
 
-            m_cli.usage( Catch::cout(), processName );
-            Catch::cout() << "For more detail usage please see the project docs\n" << std::endl;
-        }
-        void libIdentify() {
-            Catch::cout()
-                    << std::left << std::setw(16) << "description: " << "A Catch 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;
-        }
-
-        int applyCommandLine( int argc, char const* const* const argv, OnUnusedOptions::DoWhat unusedOptionBehaviour = OnUnusedOptions::Fail ) {
-            try {
-                m_cli.setThrowOnUnrecognisedTokens( unusedOptionBehaviour == OnUnusedOptions::Fail );
-                m_unusedTokens = m_cli.parseInto( Clara::argsToVector( argc, argv ), m_configData );
-                if( m_configData.showHelp )
-                    showHelp( m_configData.processName );
-                if( m_configData.libIdentify )
-                    libIdentify();
-                m_config.reset();
-            }
-            catch( std::exception& ex ) {
-                {
-                    Colour colourGuard( Colour::Red );
-                    Catch::cerr()
-                        << "\nError(s) in input:\n"
-                        << Text( ex.what(), TextAttributes().setIndent(2) )
-                        << "\n\n";
-                }
-                m_cli.usage( Catch::cout(), m_configData.processName );
-                return (std::numeric_limits<int>::max)();
-            }
-            return 0;
+#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)
 
-        void useConfigData( ConfigData const& _configData ) {
-            m_configData = _configData;
-            m_config.reset();
-        }
+        namespace Detail {
+            inline void optimizer_barrier() {
+                std::atomic_thread_fence(std::memory_order_seq_cst);
+            }
+        } // namespace Detail
 
-        int run( int argc, char const* const* const argv ) {
+#endif
 
-            int returnCode = applyCommandLine( argc, argv );
-            if( returnCode == 0 )
-                returnCode = run();
-            return returnCode;
+        template <typename T>
+        inline void deoptimize_value(T&& x) {
+            keep_memory(&x);
         }
 
-    #if defined(WIN32) && defined(UNICODE)
-        int run( int argc, wchar_t const* const* const argv ) {
+        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...)));
+        }
 
-            char **utf8Argv = new char *[ argc ];
+        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
 
-            for ( int i = 0; i < argc; ++i ) {
-                int bufSize = WideCharToMultiByte( CP_UTF8, 0, argv[i], -1, NULL, 0, NULL, NULL );
+// end catch_optimizer.hpp
+// start catch_complete_invoke.hpp
 
-                utf8Argv[ i ] = new char[ bufSize ];
+// Invoke with a special case for void
 
-                WideCharToMultiByte( CP_UTF8, 0, argv[i], -1, utf8Argv[i], bufSize, NULL, NULL );
-            }
 
-            int returnCode = applyCommandLine( argc, utf8Argv );
-            if( returnCode == 0 )
-                returnCode = run();
+#include <type_traits>
+#include <utility>
 
-            for ( int i = 0; i < argc; ++i )
-                delete [] utf8Argv[ i ];
+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 {};
+                }
+            };
 
-            delete [] utf8Argv;
+            // invoke and not return void :(
+            template <typename Fun, typename... Args>
+            CompleteType_t<FunctionReturnType<Fun, Args...>> complete_invoke(Fun&& fun, Args&&... args) {
+                return CompleteInvoker<FunctionReturnType<Fun, Args...>>::invoke(std::forward<Fun>(fun), std::forward<Args>(args)...);
+            }
 
-            return returnCode;
-        }
-    #endif
+            const std::string benchmarkErrorMsg = "a benchmark failed to run successfully";
+        } // namespace Detail
 
-        int run() {
-            if( ( m_configData.waitForKeypress & WaitForKeypress::BeforeStart ) != 0 ) {
-                Catch::cout() << "...waiting for enter/ return before starting" << std::endl;
-                static_cast<void>(std::getchar());
+        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);
             }
-            int exitCode = runInternal();
-            if( ( m_configData.waitForKeypress & WaitForKeypress::BeforeExit ) != 0 ) {
-                Catch::cout() << "...waiting for enter/ return before exiting, with code: " << exitCode << std::endl;
-                static_cast<void>(std::getchar());
-            }
-            return exitCode;
         }
+    } // namespace Benchmark
+} // namespace Catch
 
-        Clara::CommandLine<ConfigData> const& cli() const {
-            return m_cli;
-        }
-        std::vector<Clara::Parser::Token> const& unusedTokens() const {
-            return m_unusedTokens;
-        }
-        ConfigData& configData() {
-            return m_configData;
-        }
-        Config& config() {
-            if( !m_config )
-                m_config = new Config( m_configData );
-            return *m_config;
-        }
-    private:
+// 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(); }
 
-        int runInternal() {
-            if( m_configData.showHelp || m_configData.libIdentify )
-                return 0;
+                ClockDuration<Clock> elapsed() const { return finished - started; }
 
-            try
-            {
-                config(); // Force config to be constructed
+                TimePoint<Clock> started;
+                TimePoint<Clock> finished;
+            };
+        } // namespace Detail
 
-                seedRng( *m_config );
+        struct Chronometer {
+        public:
+            template <typename Fun>
+            void measure(Fun&& fun) { measure(std::forward<Fun>(fun), is_callable<Fun(int)>()); }
 
-                if( m_configData.filenamesAsTags )
-                    applyFilenamesAsTags( *m_config );
+            int runs() const { return k; }
 
-                // Handle list request
-                if( Option<std::size_t> listed = list( config() ) )
-                    return static_cast<int>( *listed );
+            Chronometer(Detail::ChronometerConcept& meter, int k)
+                : impl(&meter)
+                , k(k) {}
 
-                return static_cast<int>( runTests( m_config ).assertions.failed );
-            }
-            catch( std::exception& ex ) {
-                Catch::cerr() << ex.what() << std::endl;
-                return (std::numeric_limits<int>::max)();
+        private:
+            template <typename Fun>
+            void measure(Fun&& fun, std::false_type) {
+                measure([&fun](int) { return fun(); }, std::true_type());
             }
-        }
 
-        Clara::CommandLine<ConfigData> m_cli;
-        std::vector<Clara::Parser::Token> m_unusedTokens;
-        ConfigData m_configData;
-        Ptr<Config> m_config;
-    };
-
-    bool Session::alreadyInstantiated = false;
+            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();
+            }
 
-} // end namespace Catch
+            Detail::ChronometerConcept* impl;
+            int k;
+        };
+    } // namespace Benchmark
+} // namespace Catch
 
-// #included from: catch_registry_hub.hpp
-#define TWOBLUECUBES_CATCH_REGISTRY_HUB_HPP_INCLUDED
+// end catch_chronometer.hpp
+// start catch_environment.hpp
 
-// #included from: catch_test_case_registry_impl.hpp
-#define TWOBLUECUBES_CATCH_TEST_CASE_REGISTRY_IMPL_HPP_INCLUDED
+// Environment information
 
-#include <vector>
-#include <set>
-#include <sstream>
-#include <algorithm>
 
 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
 
-    struct RandomNumberGenerator {
-        typedef unsigned int result_type;
+// end catch_environment.hpp
+// start catch_execution_plan.hpp
 
-        result_type operator()( result_type n ) const { return std::rand() % n; }
+ // Execution plan
 
-#ifdef CATCH_CONFIG_CPP11_SHUFFLE
-        static constexpr result_type (min)() { return 0; }
-        static constexpr result_type (max)() { return 1000000; }
-        result_type operator()() const { return std::rand() % (max)(); }
-#endif
-        template<typename V>
-        static void shuffle( V& vector ) {
-            RandomNumberGenerator rng;
-#ifdef CATCH_CONFIG_CPP11_SHUFFLE
-            std::shuffle( vector.begin(), vector.end(), rng );
-#else
-            std::random_shuffle( vector.begin(), vector.end(), rng );
-#endif
-        }
-    };
 
-    inline std::vector<TestCase> sortTests( IConfig const& config, std::vector<TestCase> const& unsortedTestCases ) {
+// start catch_benchmark_function.hpp
 
-        std::vector<TestCase> sorted = unsortedTestCases;
+ // Dumb std::function implementation for consistent call overhead
 
-        switch( config.runOrder() ) {
-            case RunTests::InLexicographicalOrder:
-                std::sort( sorted.begin(), sorted.end() );
-                break;
-            case RunTests::InRandomOrder:
-                {
-                    seedRng( config );
-                    RandomNumberGenerator::shuffle( sorted );
-                }
-                break;
-            case RunTests::InDeclarationOrder:
-                // already in declaration order
-                break;
-        }
-        return sorted;
-    }
-    bool matchTest( TestCase const& testCase, TestSpec const& testSpec, IConfig const& config ) {
-        return testSpec.matches( testCase ) && ( config.allowThrows() || !testCase.throws() );
-    }
 
-    void enforceNoDuplicateTestCases( std::vector<TestCase> const& functions ) {
-        std::set<TestCase> seenFunctions;
-        for( std::vector<TestCase>::const_iterator it = functions.begin(), itEnd = functions.end();
-            it != itEnd;
-            ++it ) {
-            std::pair<std::set<TestCase>::const_iterator, bool> prev = seenFunctions.insert( *it );
-            if( !prev.second ) {
-                std::ostringstream ss;
+#include <cassert>
+#include <type_traits>
+#include <utility>
+#include <memory>
 
-                ss  << Colour( Colour::Red )
-                    << "error: TEST_CASE( \"" << it->name << "\" ) already defined.\n"
-                    << "\tFirst seen at " << prev.first->getTestCaseInfo().lineInfo << '\n'
-                    << "\tRedefined at " << it->getTestCaseInfo().lineInfo << std::endl;
+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);
+                    }
 
-                throw std::runtime_error(ss.str());
-            }
-        }
-    }
+                    Fun fun;
+                };
 
-    std::vector<TestCase> filterTests( std::vector<TestCase> const& testCases, TestSpec const& testSpec, IConfig const& config ) {
-        std::vector<TestCase> filtered;
-        filtered.reserve( testCases.size() );
-        for( std::vector<TestCase>::const_iterator it = testCases.begin(), itEnd = testCases.end();
-                it != itEnd;
-                ++it )
-            if( matchTest( *it, testSpec, config ) )
-                filtered.push_back( *it );
-        return filtered;
-    }
-    std::vector<TestCase> const& getAllTestCasesSorted( IConfig const& config ) {
-        return getRegistryHub().getTestCaseRegistry().getAllTestsSorted( config );
-    }
+                struct do_nothing { void operator()() const {} };
 
-    class TestRegistry : public ITestCaseRegistry {
-    public:
-        TestRegistry()
-        :   m_currentSortOrder( RunTests::InDeclarationOrder ),
-            m_unnamedCount( 0 )
-        {}
-        virtual ~TestRegistry();
+                template <typename T>
+                BenchmarkFunction(model<T>* c) : f(c) {}
 
-        virtual void registerTest( TestCase const& testCase ) {
-            std::string name = testCase.getTestCaseInfo().name;
-            if( name.empty() ) {
-                std::ostringstream oss;
-                oss << "Anonymous test case " << ++m_unnamedCount;
-                return registerTest( testCase.withName( oss.str() ) );
-            }
-            m_functions.push_back( testCase );
-        }
+            public:
+                BenchmarkFunction()
+                    : f(new model<do_nothing>{ {} }) {}
 
-        virtual std::vector<TestCase> const& getAllTests() const {
-            return m_functions;
-        }
-        virtual std::vector<TestCase> const& getAllTestsSorted( IConfig const& config ) const {
-            if( m_sortedFunctions.empty() )
-                enforceNoDuplicateTestCases( m_functions );
+                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))) {}
 
-            if(  m_currentSortOrder != config.runOrder() || m_sortedFunctions.empty() ) {
-                m_sortedFunctions = sortTests( config, m_functions );
-                m_currentSortOrder = config.runOrder();
-            }
-            return m_sortedFunctions;
-        }
+                BenchmarkFunction(BenchmarkFunction&& that)
+                    : f(std::move(that.f)) {}
 
-    private:
-        std::vector<TestCase> m_functions;
-        mutable RunTests::InWhatOrder m_currentSortOrder;
-        mutable std::vector<TestCase> m_sortedFunctions;
-        size_t m_unnamedCount;
-        std::ios_base::Init m_ostreamInit; // Forces cout/ cerr to be initialised
-    };
+                BenchmarkFunction(BenchmarkFunction const& that)
+                    : f(that.f->clone()) {}
 
-    ///////////////////////////////////////////////////////////////////////////
+                BenchmarkFunction& operator=(BenchmarkFunction&& that) {
+                    f = std::move(that.f);
+                    return *this;
+                }
 
-    class FreeFunctionTestCase : public SharedImpl<ITestCase> {
-    public:
+                BenchmarkFunction& operator=(BenchmarkFunction const& that) {
+                    f.reset(that.f->clone());
+                    return *this;
+                }
 
-        FreeFunctionTestCase( TestFunction fun ) : m_fun( fun ) {}
+                void operator()(Chronometer meter) const { f->call(meter); }
 
-        virtual void invoke() const {
-            m_fun();
-        }
+            private:
+                std::unique_ptr<callable> f;
+            };
+        } // namespace Detail
+    } // namespace Benchmark
+} // namespace Catch
 
-    private:
-        virtual ~FreeFunctionTestCase();
+// end catch_benchmark_function.hpp
+// start catch_repeat.hpp
 
-        TestFunction m_fun;
-    };
+// repeat algorithm
 
-    inline std::string extractClassName( std::string const& classOrQualifiedMethodName ) {
-        std::string className = classOrQualifiedMethodName;
-        if( startsWith( className, '&' ) )
-        {
-            std::size_t lastColons = className.rfind( "::" );
-            std::size_t penultimateColons = className.rfind( "::", lastColons-1 );
-            if( penultimateColons == std::string::npos )
-                penultimateColons = 1;
-            className = className.substr( penultimateColons, lastColons-penultimateColons );
-        }
-        return className;
-    }
 
-    void registerTestCase
-        (   ITestCase* testCase,
-            char const* classOrQualifiedMethodName,
-            NameAndDesc const& nameAndDesc,
-            SourceLineInfo const& lineInfo ) {
+#include <type_traits>
+#include <utility>
 
-        getMutableRegistryHub().registerTest
-            ( makeTestCase
-                (   testCase,
-                    extractClassName( classOrQualifiedMethodName ),
-                    nameAndDesc.name,
-                    nameAndDesc.description,
-                    lineInfo ) );
-    }
-    void registerTestCaseFunction
-        (   TestFunction function,
-            SourceLineInfo const& lineInfo,
-            NameAndDesc const& nameAndDesc ) {
-        registerTestCase( new FreeFunctionTestCase( function ), "", nameAndDesc, lineInfo );
-    }
+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
 
-    AutoReg::AutoReg
-        (   TestFunction function,
-            SourceLineInfo const& lineInfo,
-            NameAndDesc const& nameAndDesc ) {
-        registerTestCaseFunction( function, lineInfo, nameAndDesc );
-    }
+// Run a function for a minimum amount of time
 
-    AutoReg::~AutoReg() {}
 
-} // end namespace Catch
+// start catch_measure.hpp
 
-// #included from: catch_reporter_registry.hpp
-#define TWOBLUECUBES_CATCH_REPORTER_REGISTRY_HPP_INCLUDED
+// Measure
 
-#include <map>
 
-namespace Catch {
+// start catch_timing.hpp
 
-    class ReporterRegistry : public IReporterRegistry {
+// Timing
 
-    public:
 
-        virtual ~ReporterRegistry() CATCH_OVERRIDE {}
-
-        virtual IStreamingReporter* create( std::string const& name, Ptr<IConfig const> const& config ) const CATCH_OVERRIDE {
-            FactoryMap::const_iterator it =  m_factories.find( name );
-            if( it == m_factories.end() )
-                return CATCH_NULL;
-            return it->second->create( ReporterConfig( config ) );
-        }
-
-        void registerReporter( std::string const& name, Ptr<IReporterFactory> const& factory ) {
-            m_factories.insert( std::make_pair( name, factory ) );
-        }
-        void registerListener( Ptr<IReporterFactory> const& factory ) {
-            m_listeners.push_back( factory );
-        }
+#include <tuple>
+#include <type_traits>
 
-        virtual FactoryMap const& getFactories() const CATCH_OVERRIDE {
-            return m_factories;
-        }
-        virtual Listeners const& getListeners() const CATCH_OVERRIDE {
-            return m_listeners;
-        }
+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
 
-    private:
-        FactoryMap m_factories;
-        Listeners m_listeners;
-    };
-}
+// end catch_timing.hpp
+#include <utility>
 
-// #included from: catch_exception_translator_registry.hpp
-#define TWOBLUECUBES_CATCH_EXCEPTION_TRANSLATOR_REGISTRY_HPP_INCLUDED
+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
 
-#ifdef __OBJC__
-#import "Foundation/Foundation.h"
-#endif
+// 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));
 
-    class ExceptionTranslatorRegistry : public IExceptionTranslatorRegistry {
-    public:
-        ~ExceptionTranslatorRegistry() {
-            deleteAll( m_translators );
-        }
+                return { meter.elapsed(), std::move(result), iters };
+            }
 
-        virtual void registerTranslator( const IExceptionTranslator* translator ) {
-            m_translators.push_back( translator );
-        }
+            template <typename Clock, typename Fun>
+            using run_for_at_least_argument_t = typename std::conditional<is_callable<Fun(Chronometer)>::value, Chronometer, int>::type;
 
-        virtual std::string translateActiveException() const {
-            try {
-#ifdef __OBJC__
-                // In Objective-C try objective-c exceptions first
-                @try {
-                    return tryTranslators();
+            struct optimized_away_error : std::exception {
+                const char* what() const noexcept override {
+                    return "could not measure benchmark, maybe it was optimized away";
                 }
-                @catch (NSException *exception) {
-                    return Catch::toString( [exception description] );
+            };
+
+            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;
                 }
-#else
-                return tryTranslators();
-#endif
-            }
-            catch( TestFailureException& ) {
-                throw;
-            }
-            catch( std::exception& ex ) {
-                return ex.what();
+                Catch::throw_exception(optimized_away_error{});
             }
-            catch( std::string& msg ) {
-                return msg;
-            }
-            catch( const char* msg ) {
-                return msg;
-            }
-            catch(...) {
-                return "Unknown exception";
+        } // 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 };
             }
-        }
 
-        std::string tryTranslators() const {
-            if( m_translators.empty() )
-                throw;
-            else
-                return m_translators[0]->translate( m_translators.begin()+1, m_translators.end() );
-        }
+            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
 
-    private:
-        std::vector<const IExceptionTranslator*> m_translators;
-    };
-}
+// end catch_execution_plan.hpp
+// start catch_estimate_clock.hpp
 
-// #included from: catch_tag_alias_registry.h
-#define TWOBLUECUBES_CATCH_TAG_ALIAS_REGISTRY_H_INCLUDED
+ // Environment measurement
 
-#include <map>
 
-namespace Catch {
+// start catch_stats.hpp
 
-    class TagAliasRegistry : public ITagAliasRegistry {
-    public:
-        virtual ~TagAliasRegistry();
-        virtual Option<TagAlias> find( std::string const& alias ) const;
-        virtual std::string expandAliases( std::string const& unexpandedTestSpec ) const;
-        void add( std::string const& alias, std::string const& tag, SourceLineInfo const& lineInfo );
+// Statistical analysis tools
 
-    private:
-        std::map<std::string, TagAlias> m_registry;
-    };
 
-} // end namespace Catch
+#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;
+            }
 
-    namespace {
+            template <typename Iterator>
+            double mean(Iterator first, Iterator last) {
+                auto count = last - first;
+                double sum = std::accumulate(first, last, 0.);
+                return sum / count;
+            }
 
-        class RegistryHub : public IRegistryHub, public IMutableRegistryHub {
+            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;
+            }
 
-            RegistryHub( RegistryHub const& );
-            void operator=( RegistryHub const& );
+            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);
 
-        public: // IRegistryHub
-            RegistryHub() {
-            }
-            virtual IReporterRegistry const& getReporterRegistry() const CATCH_OVERRIDE {
-                return m_reporterRegistry;
-            }
-            virtual ITestCaseRegistry const& getTestCaseRegistry() const CATCH_OVERRIDE {
-                return m_testCaseRegistry;
-            }
-            virtual IExceptionTranslatorRegistry& getExceptionTranslatorRegistry() CATCH_OVERRIDE {
-                return m_exceptionTranslatorRegistry;
+                for (auto it = first; it != last; ++it) {
+                    std::iter_swap(it, first);
+                    results.push_back(estimator(second, last));
+                }
+
+                return results;
             }
-            virtual ITagAliasRegistry const& getTagAliasRegistry() const CATCH_OVERRIDE {
-                return m_tagAliasRegistry;
+
+            inline double normal_cdf(double x) {
+                return std::erfc(-x / std::sqrt(2.0)) / 2.0;
             }
 
-        public: // IMutableRegistryHub
-            virtual void registerReporter( std::string const& name, Ptr<IReporterFactory> const& factory ) CATCH_OVERRIDE {
-                m_reporterRegistry.registerReporter( name, factory );
+            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 };
             }
-            virtual void registerListener( Ptr<IReporterFactory> const& factory ) CATCH_OVERRIDE {
-                m_reporterRegistry.registerListener( factory );
+
+            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;
             }
-            virtual void registerTest( TestCase const& testInfo ) CATCH_OVERRIDE {
-                m_testCaseRegistry.registerTest( testInfo );
+
+            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;
             }
-            virtual void registerTranslator( const IExceptionTranslator* translator ) CATCH_OVERRIDE {
-                m_exceptionTranslatorRegistry.registerTranslator( translator );
+            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()),
+                };
             }
-            virtual void registerTagAlias( std::string const& alias, std::string const& tag, SourceLineInfo const& lineInfo ) CATCH_OVERRIDE {
-                m_tagAliasRegistry.add( alias, tag, lineInfo );
+            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()),
+                };
             }
 
-        private:
-            TestRegistry m_testCaseRegistry;
-            ReporterRegistry m_reporterRegistry;
-            ExceptionTranslatorRegistry m_exceptionTranslatorRegistry;
-            TagAliasRegistry m_tagAliasRegistry;
-        };
-
-        // Single, global, instance
-        inline RegistryHub*& getTheRegistryHub() {
-            static RegistryHub* theRegistryHub = CATCH_NULL;
-            if( !theRegistryHub )
-                theRegistryHub = new RegistryHub();
-            return theRegistryHub;
-        }
-    }
-
-    IRegistryHub& getRegistryHub() {
-        return *getTheRegistryHub();
-    }
-    IMutableRegistryHub& getMutableRegistryHub() {
-        return *getTheRegistryHub();
-    }
-    void cleanUp() {
-        delete getTheRegistryHub();
-        getTheRegistryHub() = CATCH_NULL;
-        cleanUpContext();
-    }
-    std::string translateActiveException() {
-        return getRegistryHub().getExceptionTranslatorRegistry().translateActiveException();
-    }
-
-} // end namespace Catch
+            template <typename Clock>
+            Environment<FloatDuration<Clock>> measure_environment() {
+                static Environment<FloatDuration<Clock>>* env = nullptr;
+                if (env) {
+                    return *env;
+                }
 
-// #included from: catch_notimplemented_exception.hpp
-#define TWOBLUECUBES_CATCH_NOTIMPLEMENTED_EXCEPTION_HPP_INCLUDED
+                auto iters = Detail::warmup<Clock>();
+                auto resolution = Detail::estimate_clock_resolution<Clock>(iters);
+                auto cost = Detail::estimate_clock_cost<Clock>(resolution.mean);
 
-#include <sstream>
+                env = new Environment<FloatDuration<Clock>>{ resolution, cost };
+                return *env;
+            }
+        } // namespace Detail
+    } // namespace Benchmark
+} // namespace Catch
 
-namespace Catch {
+// end catch_estimate_clock.hpp
+// start catch_analyse.hpp
 
-    NotImplementedException::NotImplementedException( SourceLineInfo const& lineInfo )
-    :   m_lineInfo( lineInfo ) {
-        std::ostringstream oss;
-        oss << lineInfo << ": function ";
-        oss << "not implemented";
-        m_what = oss.str();
-    }
+ // Run and analyse one benchmark
 
-    const char* NotImplementedException::what() const CATCH_NOEXCEPT {
-        return m_what.c_str();
-    }
 
-} // end namespace Catch
+// start catch_sample_analysis.hpp
 
-// #included from: catch_context_impl.hpp
-#define TWOBLUECUBES_CATCH_CONTEXT_IMPL_HPP_INCLUDED
+// Benchmark results
 
-// #included from: catch_stream.hpp
-#define TWOBLUECUBES_CATCH_STREAM_HPP_INCLUDED
 
-#include <stdexcept>
-#include <cstdio>
-#include <iostream>
+#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
 
-    template<typename WriterF, size_t bufferSize=256>
-    class StreamBufImpl : public StreamBufBase {
-        char data[bufferSize];
-        WriterF m_writer;
-
-    public:
-        StreamBufImpl() {
-            setp( data, data + sizeof(data) );
-        }
+// end catch_sample_analysis.hpp
+#include <algorithm>
+#include <iterator>
+#include <vector>
 
-        ~StreamBufImpl() CATCH_NOEXCEPT {
-            sync();
-        }
+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
 
-    private:
-        int overflow( int c ) {
-            sync();
+// end catch_analyse.hpp
+#include <algorithm>
+#include <functional>
+#include <string>
+#include <vector>
+#include <cmath>
 
-            if( c != EOF ) {
-                if( pbase() == epptr() )
-                    m_writer( std::string( 1, static_cast<char>( c ) ) );
-                else
-                    sputc( static_cast<char>( c ) );
+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 };
             }
-            return 0;
-        }
 
-        int sync() {
-            if( pbase() != pptr() ) {
-                m_writer( std::string( pbase(), static_cast<std::string::size_type>( pptr() - pbase() ) ) );
-                setp( pbase(), epptr() );
+            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());
+                }
             }
-            return 0;
-        }
-    };
 
-    ///////////////////////////////////////////////////////////////////////////
+            // 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;
+            }
 
-    FileStream::FileStream( std::string const& filename ) {
-        m_ofs.open( filename.c_str() );
-        if( m_ofs.fail() ) {
-            std::ostringstream oss;
-            oss << "Unable to open file: '" << filename << '\'';
-            throw std::domain_error( oss.str() );
-        }
-    }
+            explicit operator bool() {
+                return true;
+            }
 
-    std::ostream& FileStream::stream() const {
-        return m_ofs;
+        private:
+            Detail::BenchmarkFunction fun;
+            std::string name;
+        };
     }
+} // namespace Catch
 
-    struct OutputDebugWriter {
+#define INTERNAL_CATCH_GET_1_ARG(arg1, arg2, ...) arg1
+#define INTERNAL_CATCH_GET_2_ARG(arg1, arg2, ...) arg2
 
-        void operator()( std::string const&str ) {
-            writeToDebugConsole( str );
-        }
-    };
+#define INTERNAL_CATCH_BENCHMARK(BenchmarkName, name, benchmarkIndex)\
+    if( Catch::Benchmark::Benchmark BenchmarkName{name} ) \
+        BenchmarkName = [&](int benchmarkIndex)
 
-    DebugOutStream::DebugOutStream()
-    :   m_streamBuf( new StreamBufImpl<OutputDebugWriter>() ),
-        m_os( m_streamBuf.get() )
-    {}
+#define INTERNAL_CATCH_BENCHMARK_ADVANCED(BenchmarkName, name)\
+    if( Catch::Benchmark::Benchmark BenchmarkName{name} ) \
+        BenchmarkName = [&]
 
-    std::ostream& DebugOutStream::stream() const {
-        return m_os;
-    }
+// end catch_benchmark.hpp
+// start catch_constructor.hpp
 
-    // Store the streambuf from cout up-front because
-    // cout may get redirected when running tests
-    CoutStream::CoutStream()
-    :   m_os( Catch::cout().rdbuf() )
-    {}
+// Constructor and destructor helpers
 
-    std::ostream& CoutStream::stream() const {
-        return m_os;
-    }
 
-#ifndef CATCH_CONFIG_NOSTDOUT // If you #define this you must implement these functions
-    std::ostream& cout() {
-        return std::cout;
-    }
-    std::ostream& cerr() {
-        return std::cerr;
-    }
-    std::ostream& clog() {
-        return std::clog;
-    }
-#endif
-}
+#include <type_traits>
 
 namespace Catch {
+    namespace Benchmark {
+        namespace Detail {
+            template <typename T, bool Destruct>
+            struct ObjectStorage
+            {
+                ObjectStorage() : data() {}
 
-    class Context : public IMutableContext {
-
-        Context() : m_config( CATCH_NULL ), m_runner( CATCH_NULL ), m_resultCapture( CATCH_NULL ) {}
-        Context( Context const& );
-        void operator=( Context const& );
+                ObjectStorage(const ObjectStorage& other)
+                {
+                    new(&data) T(other.stored_object());
+                }
 
-    public:
-        virtual ~Context() {
-            deleteAllValues( m_generatorsByTestName );
-        }
+                ObjectStorage(ObjectStorage&& other)
+                {
+                    new(&data) T(std::move(other.stored_object()));
+                }
 
-    public: // IContext
-        virtual IResultCapture* getResultCapture() {
-            return m_resultCapture;
-        }
-        virtual IRunner* getRunner() {
-            return m_runner;
-        }
-        virtual size_t getGeneratorIndex( std::string const& fileInfo, size_t totalSize ) {
-            return getGeneratorsForCurrentTest()
-            .getGeneratorInfo( fileInfo, totalSize )
-            .getCurrentIndex();
-        }
-        virtual bool advanceGeneratorsForCurrentTest() {
-            IGeneratorsForTest* generators = findGeneratorsForCurrentTest();
-            return generators && generators->moveNext();
-        }
+                ~ObjectStorage() { destruct_on_exit<T>(); }
 
-        virtual Ptr<IConfig const> getConfig() const {
-            return m_config;
-        }
+                template <typename... Args>
+                void construct(Args&&... args)
+                {
+                    new (&data) T(std::forward<Args>(args)...);
+                }
 
-    public: // IMutableContext
-        virtual void setResultCapture( IResultCapture* resultCapture ) {
-            m_resultCapture = resultCapture;
-        }
-        virtual void setRunner( IRunner* runner ) {
-            m_runner = runner;
-        }
-        virtual void setConfig( Ptr<IConfig const> const& config ) {
-            m_config = config;
-        }
+                template <bool AllowManualDestruction = !Destruct>
+                typename std::enable_if<AllowManualDestruction>::type destruct()
+                {
+                    stored_object().~T();
+                }
 
-        friend IMutableContext& getCurrentMutableContext();
+            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) { }
 
-    private:
-        IGeneratorsForTest* findGeneratorsForCurrentTest() {
-            std::string testName = getResultCapture()->getCurrentTestName();
+                T& stored_object() {
+                    return *static_cast<T*>(static_cast<void*>(&data));
+                }
 
-            std::map<std::string, IGeneratorsForTest*>::const_iterator it =
-                m_generatorsByTestName.find( testName );
-            return it != m_generatorsByTestName.end()
-                ? it->second
-                : CATCH_NULL;
-        }
+                T const& stored_object() const {
+                    return *static_cast<T*>(static_cast<void*>(&data));
+                }
 
-        IGeneratorsForTest& getGeneratorsForCurrentTest() {
-            IGeneratorsForTest* generators = findGeneratorsForCurrentTest();
-            if( !generators ) {
-                std::string testName = getResultCapture()->getCurrentTestName();
-                generators = createGeneratorsForTest();
-                m_generatorsByTestName.insert( std::make_pair( testName, generators ) );
-            }
-            return *generators;
+                struct { alignas(T) unsigned char data[sizeof(T)]; }  data;
+            };
         }
 
-    private:
-        Ptr<IConfig const> m_config;
-        IRunner* m_runner;
-        IResultCapture* m_resultCapture;
-        std::map<std::string, IGeneratorsForTest*> m_generatorsByTestName;
-    };
-
-    namespace {
-        Context* currentContext = CATCH_NULL;
-    }
-    IMutableContext& getCurrentMutableContext() {
-        if( !currentContext )
-            currentContext = new Context();
-        return *currentContext;
-    }
-    IContext& getCurrentContext() {
-        return getCurrentMutableContext();
-    }
+        template <typename T>
+        using storage_for = Detail::ObjectStorage<T, true>;
 
-    void cleanUpContext() {
-        delete currentContext;
-        currentContext = CATCH_NULL;
+        template <typename T>
+        using destructable_object = Detail::ObjectStorage<T, false>;
     }
 }
 
-// #included from: catch_console_colour_impl.hpp
-#define TWOBLUECUBES_CATCH_CONSOLE_COLOUR_IMPL_HPP_INCLUDED
+// end catch_constructor.hpp
+// end catch_benchmarking_all.hpp
+#endif
 
-// #included from: catch_errno_guard.hpp
-#define TWOBLUECUBES_CATCH_ERRNO_GUARD_HPP_INCLUDED
+#endif // ! CATCH_CONFIG_IMPL_ONLY
 
-#include <cerrno>
+#ifdef CATCH_IMPL
+// start catch_impl.hpp
 
-namespace Catch {
+#ifdef __clang__
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wweak-vtables"
+#endif
 
-    class ErrnoGuard {
-    public:
-        ErrnoGuard():m_oldErrno(errno){}
-        ~ErrnoGuard() { errno = m_oldErrno; }
-    private:
-        int m_oldErrno;
-    };
+// Keep these here for external reporters
+// start catch_test_case_tracker.h
 
-}
+#include <string>
+#include <vector>
+#include <memory>
 
 namespace Catch {
-    namespace {
+namespace TestCaseTracking {
 
-        struct IColourImpl {
-            virtual ~IColourImpl() {}
-            virtual void use( Colour::Code _colourCode ) = 0;
-        };
-
-        struct NoColourImpl : IColourImpl {
-            void use( Colour::Code ) {}
-
-            static IColourImpl* instance() {
-                static NoColourImpl s_instance;
-                return &s_instance;
-            }
-        };
+    struct NameAndLocation {
+        std::string name;
+        SourceLineInfo location;
 
-    } // anon namespace
-} // namespace Catch
+        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;
+        }
+    };
 
-#if !defined( CATCH_CONFIG_COLOUR_NONE ) && !defined( CATCH_CONFIG_COLOUR_WINDOWS ) && !defined( CATCH_CONFIG_COLOUR_ANSI )
-#   ifdef CATCH_PLATFORM_WINDOWS
-#       define CATCH_CONFIG_COLOUR_WINDOWS
-#   else
-#       define CATCH_CONFIG_COLOUR_ANSI
-#   endif
-#endif
+    class ITracker;
 
-#if defined ( CATCH_CONFIG_COLOUR_WINDOWS ) /////////////////////////////////////////
+    using ITrackerPtr = std::shared_ptr<ITracker>;
 
-namespace Catch {
-namespace {
+    class  ITracker {
+        NameAndLocation m_nameAndLocation;
 
-    class Win32ColourImpl : public IColourImpl {
     public:
-        Win32ColourImpl() : stdoutHandle( GetStdHandle(STD_OUTPUT_HANDLE) )
-        {
-            CONSOLE_SCREEN_BUFFER_INFO csbiInfo;
-            GetConsoleScreenBufferInfo( stdoutHandle, &csbiInfo );
-            originalForegroundAttributes = csbiInfo.wAttributes & ~( BACKGROUND_GREEN | BACKGROUND_RED | BACKGROUND_BLUE | BACKGROUND_INTENSITY );
-            originalBackgroundAttributes = csbiInfo.wAttributes & ~( FOREGROUND_GREEN | FOREGROUND_RED | FOREGROUND_BLUE | FOREGROUND_INTENSITY );
+        ITracker(NameAndLocation const& nameAndLoc) :
+            m_nameAndLocation(nameAndLoc)
+        {}
+
+        // static queries
+        NameAndLocation const& nameAndLocation() const {
+            return m_nameAndLocation;
         }
 
-        virtual void use( Colour::Code _colourCode ) {
-            switch( _colourCode ) {
-                case Colour::None:      return setTextAttribute( originalForegroundAttributes );
-                case Colour::White:     return setTextAttribute( FOREGROUND_GREEN | FOREGROUND_RED | FOREGROUND_BLUE );
-                case Colour::Red:       return setTextAttribute( FOREGROUND_RED );
-                case Colour::Green:     return setTextAttribute( FOREGROUND_GREEN );
-                case Colour::Blue:      return setTextAttribute( FOREGROUND_BLUE );
-                case Colour::Cyan:      return setTextAttribute( FOREGROUND_BLUE | FOREGROUND_GREEN );
-                case Colour::Yellow:    return setTextAttribute( FOREGROUND_RED | FOREGROUND_GREEN );
-                case Colour::Grey:      return setTextAttribute( 0 );
+        virtual ~ITracker();
 
-                case Colour::LightGrey:     return setTextAttribute( FOREGROUND_INTENSITY );
-                case Colour::BrightRed:     return setTextAttribute( FOREGROUND_INTENSITY | FOREGROUND_RED );
-                case Colour::BrightGreen:   return setTextAttribute( FOREGROUND_INTENSITY | FOREGROUND_GREEN );
-                case Colour::BrightWhite:   return setTextAttribute( FOREGROUND_INTENSITY | FOREGROUND_GREEN | FOREGROUND_RED | FOREGROUND_BLUE );
+        // 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;
 
-                case Colour::Bright: throw std::logic_error( "not a colour" );
-            }
-        }
+        virtual ITracker& parent() = 0;
 
-    private:
-        void setTextAttribute( WORD _textAttribute ) {
-            SetConsoleTextAttribute( stdoutHandle, _textAttribute | originalBackgroundAttributes );
-        }
-        HANDLE stdoutHandle;
-        WORD originalForegroundAttributes;
-        WORD originalBackgroundAttributes;
+        // actions
+        virtual void close() = 0; // Successfully complete
+        virtual void fail() = 0;
+        virtual void markAsNeedingAnotherRun() = 0;
+
+        virtual void addChild( ITrackerPtr const& child ) = 0;
+        virtual ITrackerPtr findChild( NameAndLocation const& nameAndLocation ) = 0;
+        virtual void openChild() = 0;
+
+        // Debug/ checking
+        virtual bool isSectionTracker() const = 0;
+        virtual bool isGeneratorTracker() const = 0;
     };
 
-    IColourImpl* platformColourInstance() {
-        static Win32ColourImpl s_instance;
+    class TrackerContext {
 
-        Ptr<IConfig const> config = getCurrentContext().getConfig();
-        UseColour::YesOrNo colourMode = config
-            ? config->useColour()
-            : UseColour::Auto;
-        if( colourMode == UseColour::Auto )
-            colourMode = !isDebuggerActive()
-                ? UseColour::Yes
-                : UseColour::No;
-        return colourMode == UseColour::Yes
-            ? &s_instance
-            : NoColourImpl::instance();
-    }
+        enum RunState {
+            NotStarted,
+            Executing,
+            CompletedCycle
+        };
 
-} // end anon namespace
-} // end namespace Catch
+        ITrackerPtr m_rootTracker;
+        ITracker* m_currentTracker = nullptr;
+        RunState m_runState = NotStarted;
 
-#elif defined( CATCH_CONFIG_COLOUR_ANSI ) //////////////////////////////////////
+    public:
 
-#include <unistd.h>
+        ITracker& startRun();
+        void endRun();
 
-namespace Catch {
-namespace {
+        void startCycle();
+        void completeCycle();
 
-    // use POSIX/ ANSI console terminal codes
-    // Thanks to Adam Strzelecki for original contribution
-    // (http://github.com/nanoant)
-    // https://github.com/philsquared/Catch/pull/131
-    class PosixColourImpl : public IColourImpl {
-    public:
-        virtual void use( Colour::Code _colourCode ) {
-            switch( _colourCode ) {
-                case Colour::None:
-                case Colour::White:     return setColour( "[0m" );
-                case Colour::Red:       return setColour( "[0;31m" );
-                case Colour::Green:     return setColour( "[0;32m" );
-                case Colour::Blue:      return setColour( "[0;34m" );
-                case Colour::Cyan:      return setColour( "[0;36m" );
-                case Colour::Yellow:    return setColour( "[0;33m" );
-                case Colour::Grey:      return setColour( "[1;30m" );
+        bool completedCycle() const;
+        ITracker& currentTracker();
+        void setCurrentTracker( ITracker* tracker );
+    };
 
-                case Colour::LightGrey:     return setColour( "[0;37m" );
-                case Colour::BrightRed:     return setColour( "[1;31m" );
-                case Colour::BrightGreen:   return setColour( "[1;32m" );
-                case Colour::BrightWhite:   return setColour( "[1;37m" );
+    class TrackerBase : public ITracker {
+    protected:
+        enum CycleState {
+            NotStarted,
+            Executing,
+            ExecutingChildren,
+            NeedsAnotherRun,
+            CompletedSuccessfully,
+            Failed
+        };
 
-                case Colour::Bright: throw std::logic_error( "not a colour" );
-            }
-        }
-        static IColourImpl* instance() {
-            static PosixColourImpl s_instance;
-            return &s_instance;
+        using Children = std::vector<ITrackerPtr>;
+        TrackerContext& m_ctx;
+        ITracker* m_parent;
+        Children m_children;
+        CycleState m_runState = NotStarted;
+
+    public:
+        TrackerBase( NameAndLocation const& nameAndLocation, TrackerContext& ctx, ITracker* parent );
+
+        bool isComplete() const override;
+        bool isSuccessfullyCompleted() const override;
+        bool isOpen() const override;
+        bool hasChildren() const override;
+        bool hasStarted() const override {
+            return m_runState != NotStarted;
         }
 
+        void addChild( ITrackerPtr const& child ) override;
+
+        ITrackerPtr findChild( NameAndLocation const& nameAndLocation ) override;
+        ITracker& parent() override;
+
+        void openChild() override;
+
+        bool isSectionTracker() const override;
+        bool isGeneratorTracker() const override;
+
+        void open();
+
+        void close() override;
+        void fail() override;
+        void markAsNeedingAnotherRun() override;
+
     private:
-        void setColour( const char* _escapeCode ) {
-            Catch::cout() << '\033' << _escapeCode;
-        }
+        void moveToParent();
+        void moveToThis();
     };
 
-    IColourImpl* platformColourInstance() {
-        ErrnoGuard guard;
-        Ptr<IConfig const> config = getCurrentContext().getConfig();
-        UseColour::YesOrNo colourMode = config
-            ? config->useColour()
-            : UseColour::Auto;
-        if( colourMode == UseColour::Auto )
-            colourMode = (!isDebuggerActive() && isatty(STDOUT_FILENO) )
-                ? UseColour::Yes
-                : UseColour::No;
-        return colourMode == UseColour::Yes
-            ? PosixColourImpl::instance()
-            : NoColourImpl::instance();
-    }
+    class SectionTracker : public TrackerBase {
+        std::vector<std::string> m_filters;
+        std::string m_trimmed_name;
+    public:
+        SectionTracker( NameAndLocation const& nameAndLocation, TrackerContext& ctx, ITracker* parent );
 
-} // end anon namespace
-} // end namespace Catch
+        bool isSectionTracker() const override;
 
-#else  // not Windows or ANSI ///////////////////////////////////////////////
+        bool isComplete() const override;
 
-namespace Catch {
+        static SectionTracker& acquire( TrackerContext& ctx, NameAndLocation const& nameAndLocation );
 
-    static IColourImpl* platformColourInstance() { return NoColourImpl::instance(); }
+        void tryOpen();
 
-} // end namespace Catch
+        void addInitialFilters( std::vector<std::string> const& filters );
+        void addNextFilters( std::vector<std::string> const& filters );
+        //! 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;
+    };
 
-#endif // Windows/ ANSI/ None
+} // namespace TestCaseTracking
+
+using TestCaseTracking::ITracker;
+using TestCaseTracking::TrackerContext;
+using TestCaseTracking::SectionTracker;
+
+} // namespace Catch
+
+// end catch_test_case_tracker.h
+
+// start catch_leak_detector.h
 
 namespace Catch {
 
-    Colour::Colour( Code _colourCode ) : m_moved( false ) { use( _colourCode ); }
-    Colour::Colour( Colour const& _other ) : m_moved( false ) { const_cast<Colour&>( _other ).m_moved = true; }
-    Colour::~Colour(){ if( !m_moved ) use( None ); }
+    struct LeakDetector {
+        LeakDetector();
+        ~LeakDetector();
+    };
 
-    void Colour::use( Code _colourCode ) {
-        static IColourImpl* impl = platformColourInstance();
-        impl->use( _colourCode );
-    }
+}
+// end catch_leak_detector.h
+// Cpp files will be included in the single-header file here
+// start catch_stats.cpp
 
-} // end namespace Catch
+// Statistical analysis tools
 
-// #included from: catch_generators_impl.hpp
-#define TWOBLUECUBES_CATCH_GENERATORS_IMPL_HPP_INCLUDED
+#if defined(CATCH_CONFIG_ENABLE_BENCHMARKING)
 
-#include <vector>
-#include <string>
-#include <map>
+#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);
+            }
 
-    struct GeneratorInfo : IGeneratorInfo {
+            double erfc_inv(double x) {
+                return erf_inv(1.0 - x);
+            }
 
-        GeneratorInfo( std::size_t size )
-        :   m_size( size ),
-            m_currentIndex( 0 )
-        {}
+            double normal_quantile(double p) {
+                static const double ROOT_TWO = std::sqrt(2.0);
 
-        bool moveNext() {
-            if( ++m_currentIndex == m_size ) {
-                m_currentIndex = 0;
-                return false;
+                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;
             }
-            return true;
-        }
 
-        std::size_t getCurrentIndex() const {
-            return m_currentIndex;
-        }
+            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;
+            }
 
-        std::size_t m_size;
-        std::size_t m_currentIndex;
-    };
+            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++
 
-    class GeneratorsForTest : public IGeneratorsForTest {
+                auto mean = &Detail::mean<std::vector<double>::iterator>;
+                auto stddev = &standard_deviation;
 
-    public:
-        ~GeneratorsForTest() {
-            deleteAll( m_generatorsInOrder );
-        }
+#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);
+                    });
+                };
 
-        IGeneratorInfo& getGeneratorInfo( std::string const& fileInfo, std::size_t size ) {
-            std::map<std::string, IGeneratorInfo*>::const_iterator it = m_generatorsByName.find( fileInfo );
-            if( it == m_generatorsByName.end() ) {
-                IGeneratorInfo* info = new GeneratorInfo( size );
-                m_generatorsByName.insert( std::make_pair( fileInfo, info ) );
-                m_generatorsInOrder.push_back( info );
-                return *info;
-            }
-            return *it->second;
-        }
+                auto mean_future = Estimate(mean);
+                auto stddev_future = Estimate(stddev);
 
-        bool moveNext() {
-            std::vector<IGeneratorInfo*>::const_iterator it = m_generatorsInOrder.begin();
-            std::vector<IGeneratorInfo*>::const_iterator itEnd = m_generatorsInOrder.end();
-            for(; it != itEnd; ++it ) {
-                if( (*it)->moveNext() )
-                    return true;
+                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 };
             }
-            return false;
-        }
+        } // namespace Detail
+    } // namespace Benchmark
+} // namespace Catch
 
-    private:
-        std::map<std::string, IGeneratorInfo*> m_generatorsByName;
-        std::vector<IGeneratorInfo*> m_generatorsInOrder;
-    };
+#endif // CATCH_CONFIG_ENABLE_BENCHMARKING
+// end catch_stats.cpp
+// start catch_approx.cpp
 
-    IGeneratorsForTest* createGeneratorsForTest()
-    {
-        return new GeneratorsForTest();
-    }
+#include <cmath>
+#include <limits>
 
-} // end namespace Catch
+namespace {
 
-// #included from: catch_assertionresult.hpp
-#define TWOBLUECUBES_CATCH_ASSERTIONRESULT_HPP_INCLUDED
+// 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);
+}
 
-namespace Catch {
+}
 
-    AssertionInfo::AssertionInfo():macroName(""), capturedExpression(""), resultDisposition(ResultDisposition::Normal), secondArg(""){}
+namespace Catch {
+namespace Detail {
 
-    AssertionInfo::AssertionInfo(   char const * _macroName,
-                                    SourceLineInfo const& _lineInfo,
-                                    char const * _capturedExpression,
-                                    ResultDisposition::Flags _resultDisposition,
-                                    char const * _secondArg)
-    :   macroName( _macroName ),
-        lineInfo( _lineInfo ),
-        capturedExpression( _capturedExpression ),
-        resultDisposition( _resultDisposition ),
-        secondArg( _secondArg )
+    Approx::Approx ( double value )
+    :   m_epsilon( std::numeric_limits<float>::epsilon()*100 ),
+        m_margin( 0.0 ),
+        m_scale( 0.0 ),
+        m_value( value )
     {}
 
-    AssertionResult::AssertionResult() {}
+    Approx Approx::custom() {
+        return Approx( 0 );
+    }
 
-    AssertionResult::AssertionResult( AssertionInfo const& info, AssertionResultData const& data )
-    :   m_info( info ),
-        m_resultData( data )
-    {}
+    Approx Approx::operator-() const {
+        auto temp(*this);
+        temp.m_value = -temp.m_value;
+        return temp;
+    }
 
-    AssertionResult::~AssertionResult() {}
+    std::string Approx::toString() const {
+        ReusableStringStream rss;
+        rss << "Approx( " << ::Catch::Detail::stringify( m_value ) << " )";
+        return rss.str();
+    }
 
-    // Result was a success
-    bool AssertionResult::succeeded() const {
-        return Catch::isOk( m_resultData.resultType );
+    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(std::isinf(m_value)? 0 : m_value)));
     }
 
-    // Result was a success, or failure is suppressed
-    bool AssertionResult::isOk() const {
-        return Catch::isOk( m_resultData.resultType ) || shouldSuppressFailure( m_info.resultDisposition );
+    void Approx::setMargin(double newMargin) {
+        CATCH_ENFORCE(newMargin >= 0,
+            "Invalid Approx::margin: " << newMargin << '.'
+            << " Approx::Margin has to be non-negative.");
+        m_margin = newMargin;
+    }
+
+    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 = newEpsilon;
     }
 
-    ResultWas::OfType AssertionResult::getResultType() const {
-        return m_resultData.resultType;
+} // end namespace Detail
+
+namespace literals {
+    Detail::Approx operator "" _a(long double val) {
+        return Detail::Approx(val);
+    }
+    Detail::Approx operator "" _a(unsigned long long val) {
+        return Detail::Approx(val);
+    }
+} // end namespace literals
+
+std::string StringMaker<Catch::Detail::Approx>::convert(Catch::Detail::Approx const& value) {
+    return value.toString();
+}
+
+} // end namespace Catch
+// end catch_approx.cpp
+// start catch_assertionhandler.cpp
+
+// start catch_debugger.h
+
+namespace Catch {
+    bool isDebuggerActive();
+}
+
+#ifdef CATCH_PLATFORM_MAC
+
+    #if defined(__i386__) || defined(__x86_64__)
+        #define CATCH_TRAP() __asm__("int $3\n" : : ) /* NOLINT */
+    #elif defined(__aarch64__)
+        #define CATCH_TRAP()  __asm__(".inst 0xd43e0000")
+    #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
+    // directly at the location of the failing check instead of breaking inside
+    // raise() called from it, i.e. one stack frame below.
+    #if defined(__GNUC__) && (defined(__i386) || defined(__x86_64))
+        #define CATCH_TRAP() asm volatile ("int $3") /* NOLINT */
+    #else // Fall back to the generic way.
+        #include <signal.h>
+
+        #define CATCH_TRAP() raise(SIGTRAP)
+    #endif
+#elif defined(_MSC_VER)
+    #define CATCH_TRAP() __debugbreak()
+#elif defined(__MINGW32__)
+    extern "C" __declspec(dllimport) void __stdcall DebugBreak();
+    #define CATCH_TRAP() DebugBreak()
+#endif
+
+#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
+// start catch_run_context.h
+
+// start catch_fatal_condition.h
+
+#include <cassert>
+
+namespace Catch {
+
+    // 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();
+        ~FatalConditionHandler();
+
+        void engage() {
+            assert(!m_started && "Handler cannot be installed twice.");
+            m_started = true;
+            engage_platform();
+        }
+
+        void disengage() {
+            assert(m_started && "Handler cannot be uninstalled without being installed first");
+            m_started = false;
+            disengage_platform();
+        }
+    };
+
+    //! 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();
+        }
+    };
+
+} // end namespace Catch
+
+// end catch_fatal_condition.h
+#include <string>
+
+namespace Catch {
+
+    struct IMutableContext;
+
+    ///////////////////////////////////////////////////////////////////////////
+
+    class RunContext : public IResultCapture, public IRunner {
+
+    public:
+        RunContext( RunContext const& ) = delete;
+        RunContext& operator =( RunContext const& ) = delete;
+
+        explicit RunContext( IConfigPtr const& _config, IStreamingReporterPtr&& reporter );
+
+        ~RunContext() override;
+
+        void testGroupStarting( std::string const& testSpec, std::size_t groupIndex, std::size_t groupsCount );
+        void testGroupEnded( std::string const& testSpec, Totals const& totals, std::size_t groupIndex, std::size_t groupsCount );
+
+        Totals runTest(TestCase const& testCase);
+
+        IConfigPtr config() const;
+        IStreamingReporter& reporter() const;
+
+    public: // IResultCapture
+
+        // Assertion handlers
+        void handleExpr
+                (   AssertionInfo const& info,
+                    ITransientExpression const& expr,
+                    AssertionReaction& reaction ) override;
+        void handleMessage
+                (   AssertionInfo const& info,
+                    ResultWas::OfType resultType,
+                    StringRef const& message,
+                    AssertionReaction& reaction ) override;
+        void handleUnexpectedExceptionNotThrown
+                (   AssertionInfo const& info,
+                    AssertionReaction& reaction ) override;
+        void handleUnexpectedInflightException
+                (   AssertionInfo const& info,
+                    std::string const& message,
+                    AssertionReaction& reaction ) override;
+        void handleIncomplete
+                (   AssertionInfo const& info ) override;
+        void handleNonExpr
+                (   AssertionInfo const &info,
+                    ResultWas::OfType resultType,
+                    AssertionReaction &reaction ) override;
+
+        bool sectionStarted( SectionInfo const& sectionInfo, Counts& assertions ) override;
+
+        void sectionEnded( SectionEndInfo const& endInfo ) override;
+        void sectionEndedEarly( SectionEndInfo const& endInfo ) 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 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;
+
+        void exceptionEarlyReported() override;
+
+        void handleFatalErrorCondition( StringRef message ) override;
+
+        bool lastAssertionPassed() override;
+
+        void assertionPassed() override;
+
+    public:
+        // !TBD We need to do this another way!
+        bool aborting() const final;
+
+    private:
+
+        void runCurrentTest( std::string& redirectedCout, std::string& redirectedCerr );
+        void invokeActiveTestCase();
+
+        void resetAssertionInfo();
+        bool testForMissingAssertions( Counts& assertions );
+
+        void assertionEnded( AssertionResult const& result );
+        void reportExpr
+                (   AssertionInfo const &info,
+                    ResultWas::OfType resultType,
+                    ITransientExpression const *expr,
+                    bool negated );
+
+        void populateReaction( AssertionReaction& reaction );
+
+    private:
+
+        void handleUnfinishedSections();
+
+        TestRunInfo m_runInfo;
+        IMutableContext& m_context;
+        TestCase const* m_activeTestCase = nullptr;
+        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
+namespace Catch {
+
+    namespace {
+        auto operator <<( std::ostream& os, ITransientExpression const& expr ) -> std::ostream& {
+            expr.streamReconstructedExpression( os );
+            return os;
+        }
+    }
+
+    LazyExpression::LazyExpression( bool isNegated )
+    :   m_isNegated( isNegated )
+    {}
+
+    LazyExpression::LazyExpression( LazyExpression const& other ) : m_isNegated( other.m_isNegated ) {}
+
+    LazyExpression::operator bool() const {
+        return m_transientExpression != nullptr;
+    }
+
+    auto operator << ( std::ostream& os, LazyExpression const& lazyExpr ) -> std::ostream& {
+        if( lazyExpr.m_isNegated )
+            os << "!";
+
+        if( lazyExpr ) {
+            if( lazyExpr.m_isNegated && lazyExpr.m_transientExpression->isBinaryExpression() )
+                os << "(" << *lazyExpr.m_transientExpression << ")";
+            else
+                os << *lazyExpr.m_transientExpression;
+        }
+        else {
+            os << "{** error - unchecked empty expression requested **}";
+        }
+        return os;
+    }
+
+    AssertionHandler::AssertionHandler
+        (   StringRef const& macroName,
+            SourceLineInfo const& lineInfo,
+            StringRef capturedExpression,
+            ResultDisposition::Flags resultDisposition )
+    :   m_assertionInfo{ macroName, lineInfo, capturedExpression, resultDisposition },
+        m_resultCapture( getResultCapture() )
+    {}
+
+    void AssertionHandler::handleExpr( ITransientExpression const& expr ) {
+        m_resultCapture.handleExpr( m_assertionInfo, expr, m_reaction );
+    }
+    void AssertionHandler::handleMessage(ResultWas::OfType resultType, StringRef const& message) {
+        m_resultCapture.handleMessage( m_assertionInfo, resultType, message, m_reaction );
+    }
+
+    auto AssertionHandler::allowThrows() const -> bool {
+        return getCurrentContext().getConfig()->allowThrows();
+    }
+
+    void AssertionHandler::complete() {
+        setCompleted();
+        if( m_reaction.shouldDebugBreak ) {
+
+            // If you find your debugger stopping you here then go one level up on the
+            // call-stack for the code that caused it (typically a failed assertion)
+
+            // (To go back to the test and change execution, jump over the throw, next)
+            CATCH_BREAK_INTO_DEBUGGER();
+        }
+        if (m_reaction.shouldThrow) {
+#if !defined(CATCH_CONFIG_DISABLE_EXCEPTIONS)
+            throw Catch::TestFailureException();
+#else
+            CATCH_ERROR( "Test failure requires aborting test!" );
+#endif
+        }
+    }
+    void AssertionHandler::setCompleted() {
+        m_completed = true;
+    }
+
+    void AssertionHandler::handleUnexpectedInflightException() {
+        m_resultCapture.handleUnexpectedInflightException( m_assertionInfo, Catch::translateActiveException(), m_reaction );
+    }
+
+    void AssertionHandler::handleExceptionThrownAsExpected() {
+        m_resultCapture.handleNonExpr(m_assertionInfo, ResultWas::Ok, m_reaction);
+    }
+    void AssertionHandler::handleExceptionNotThrownAsExpected() {
+        m_resultCapture.handleNonExpr(m_assertionInfo, ResultWas::Ok, m_reaction);
+    }
+
+    void AssertionHandler::handleUnexpectedExceptionNotThrown() {
+        m_resultCapture.handleUnexpectedExceptionNotThrown( m_assertionInfo, m_reaction );
+    }
+
+    void AssertionHandler::handleThrowingCallSkipped() {
+        m_resultCapture.handleNonExpr(m_assertionInfo, ResultWas::Ok, m_reaction);
+    }
+
+    // This is the overload that takes a string and infers the Equals matcher from it
+    // The more general overload, that takes any string matcher, is in catch_capture_matchers.cpp
+    void handleExceptionMatchExpr( AssertionHandler& handler, std::string const& str, StringRef const& matcherString  ) {
+        handleExceptionMatchExpr( handler, Matchers::Equals( str ), matcherString );
+    }
+
+} // namespace Catch
+// end catch_assertionhandler.cpp
+// start catch_assertionresult.cpp
+
+namespace Catch {
+    AssertionResultData::AssertionResultData(ResultWas::OfType _resultType, LazyExpression const & _lazyExpression):
+        lazyExpression(_lazyExpression),
+        resultType(_resultType) {}
+
+    std::string AssertionResultData::reconstructExpression() const {
+
+        if( reconstructedExpression.empty() ) {
+            if( lazyExpression ) {
+                ReusableStringStream rss;
+                rss << lazyExpression;
+                reconstructedExpression = rss.str();
+            }
+        }
+        return reconstructedExpression;
+    }
+
+    AssertionResult::AssertionResult( AssertionInfo const& info, AssertionResultData const& data )
+    :   m_info( info ),
+        m_resultData( data )
+    {}
+
+    // Result was a success
+    bool AssertionResult::succeeded() const {
+        return Catch::isOk( m_resultData.resultType );
+    }
+
+    // Result was a success, or failure is suppressed
+    bool AssertionResult::isOk() const {
+        return Catch::isOk( m_resultData.resultType ) || shouldSuppressFailure( m_info.resultDisposition );
+    }
+
+    ResultWas::OfType AssertionResult::getResultType() const {
+        return m_resultData.resultType;
+    }
+
+    bool AssertionResult::hasExpression() const {
+        return !m_info.capturedExpression.empty();
+    }
+
+    bool AssertionResult::hasMessage() const {
+        return !m_resultData.message.empty();
+    }
+
+    std::string AssertionResult::getExpression() const {
+        // 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.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;
+            expr += "( ";
+            expr += m_info.capturedExpression;
+            expr += " )";
+        }
+        return expr;
+    }
+
+    bool AssertionResult::hasExpandedExpression() const {
+        return hasExpression() && getExpandedExpression() != getExpression();
+    }
+
+    std::string AssertionResult::getExpandedExpression() const {
+        std::string expr = m_resultData.reconstructExpression();
+        return expr.empty()
+                ? getExpression()
+                : expr;
+    }
+
+    std::string AssertionResult::getMessage() const {
+        return m_resultData.message;
+    }
+    SourceLineInfo AssertionResult::getSourceInfo() const {
+        return m_info.lineInfo;
+    }
+
+    StringRef AssertionResult::getTestMacroName() const {
+        return m_info.macroName;
+    }
+
+} // end namespace Catch
+// end catch_assertionresult.cpp
+// start catch_capture_matchers.cpp
+
+namespace Catch {
+
+    using StringMatcher = Matchers::Impl::MatcherBase<std::string>;
+
+    // This is the general overload that takes a any string matcher
+    // There is another overload, in catch_assertionhandler.h/.cpp, that only takes a string and infers
+    // the Equals matcher (so the header does not mention matchers)
+    void handleExceptionMatchExpr( AssertionHandler& handler, StringMatcher const& matcher, StringRef const& matcherString  ) {
+        std::string exceptionMessage = Catch::translateActiveException();
+        MatchExpr<std::string, StringMatcher const&> expr( exceptionMessage, matcher, matcherString );
+        handler.handleExpr( expr );
+    }
+
+} // namespace Catch
+// end catch_capture_matchers.cpp
+// start catch_commandline.cpp
+
+// start catch_commandline.h
+
+// start catch_clara.h
+
+// Use Catch's value for console width (store Clara's off to the side, if present)
+#ifdef CLARA_CONFIG_CONSOLE_WIDTH
+#define CATCH_TEMP_CLARA_CONFIG_CONSOLE_WIDTH CATCH_CLARA_TEXTFLOW_CONFIG_CONSOLE_WIDTH
+#undef CATCH_CLARA_TEXTFLOW_CONFIG_CONSOLE_WIDTH
+#endif
+#define CATCH_CLARA_TEXTFLOW_CONFIG_CONSOLE_WIDTH CATCH_CONFIG_CONSOLE_WIDTH-1
+
+#ifdef __clang__
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wweak-vtables"
+#pragma clang diagnostic ignored "-Wexit-time-destructors"
+#pragma clang diagnostic ignored "-Wshadow"
+#endif
+
+// start clara.hpp
+// Copyright 2017 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)
+//
+// See https://github.com/philsquared/Clara for more details
+
+// Clara v1.1.5
+
+
+#ifndef CATCH_CLARA_CONFIG_CONSOLE_WIDTH
+#define CATCH_CLARA_CONFIG_CONSOLE_WIDTH 80
+#endif
+
+#ifndef CATCH_CLARA_TEXTFLOW_CONFIG_CONSOLE_WIDTH
+#define CATCH_CLARA_TEXTFLOW_CONFIG_CONSOLE_WIDTH CATCH_CLARA_CONFIG_CONSOLE_WIDTH
+#endif
+
+#ifndef CLARA_CONFIG_OPTIONAL_TYPE
+#ifdef __has_include
+#if __has_include(<optional>) && __cplusplus >= 201703L
+#include <optional>
+#define CLARA_CONFIG_OPTIONAL_TYPE std::optional
+#endif
+#endif
+#endif
+
+// ----------- #included from clara_textflow.hpp -----------
+
+// TextFlowCpp
+//
+// A single-header library for wrapping and laying out basic text, by Phil Nash
+//
+// Distributed under the Boost Software License, Version 1.0. (See accompanying
+// file LICENSE.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
+//
+// This project is hosted at https://github.com/philsquared/textflowcpp
+
+
+#include <cassert>
+#include <ostream>
+#include <sstream>
+#include <vector>
+
+#ifndef CATCH_CLARA_TEXTFLOW_CONFIG_CONSOLE_WIDTH
+#define CATCH_CLARA_TEXTFLOW_CONFIG_CONSOLE_WIDTH 80
+#endif
+
+namespace Catch {
+namespace clara {
+namespace TextFlow {
+
+inline auto isWhitespace(char c) -> bool {
+	static std::string chars = " \t\n\r";
+	return chars.find(c) != std::string::npos;
+}
+inline auto isBreakableBefore(char c) -> bool {
+	static std::string chars = "[({<|";
+	return chars.find(c) != std::string::npos;
+}
+inline auto isBreakableAfter(char c) -> bool {
+	static std::string chars = "])}>.,:;*+-=&/\\";
+	return chars.find(c) != std::string::npos;
+}
+
+class Columns;
+
+class Column {
+	std::vector<std::string> m_strings;
+	size_t m_width = CATCH_CLARA_TEXTFLOW_CONFIG_CONSOLE_WIDTH;
+	size_t m_indent = 0;
+	size_t m_initialIndent = std::string::npos;
+
+public:
+	class iterator {
+		friend Column;
+
+		Column const& m_column;
+		size_t m_stringIndex = 0;
+		size_t m_pos = 0;
+
+		size_t m_len = 0;
+		size_t m_end = 0;
+		bool m_suffix = false;
+
+		iterator(Column const& column, size_t stringIndex)
+			: m_column(column),
+			m_stringIndex(stringIndex) {}
+
+		auto line() const -> std::string const& { return m_column.m_strings[m_stringIndex]; }
+
+		auto isBoundary(size_t at) const -> bool {
+			assert(at > 0);
+			assert(at <= line().size());
+
+			return at == line().size() ||
+				(isWhitespace(line()[at]) && !isWhitespace(line()[at - 1])) ||
+				isBreakableBefore(line()[at]) ||
+				isBreakableAfter(line()[at - 1]);
+		}
+
+		void calcLength() {
+			assert(m_stringIndex < m_column.m_strings.size());
+
+			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;
+
+			if (m_end < m_pos + width) {
+				m_len = m_end - m_pos;
+			} else {
+				size_t len = width;
+				while (len > 0 && !isBoundary(m_pos + len))
+					--len;
+				while (len > 0 && isWhitespace(line()[m_pos + len - 1]))
+					--len;
+
+				if (len > 0) {
+					m_len = len;
+				} else {
+					m_suffix = true;
+					m_len = width - 1;
+				}
+			}
+		}
+
+		auto indent() const -> size_t {
+			auto initial = m_pos == 0 && m_stringIndex == 0 ? m_column.m_initialIndent : std::string::npos;
+			return initial == std::string::npos ? m_column.m_indent : initial;
+		}
+
+		auto addIndentAndSuffix(std::string const &plain) const -> std::string {
+			return std::string(indent(), ' ') + (m_suffix ? plain + "-" : plain);
+		}
+
+	public:
+		using difference_type = std::ptrdiff_t;
+		using value_type = std::string;
+		using pointer = value_type * ;
+		using reference = value_type & ;
+		using iterator_category = std::forward_iterator_tag;
+
+		explicit iterator(Column const& column) : m_column(column) {
+			assert(m_column.m_width > m_column.m_indent);
+			assert(m_column.m_initialIndent == std::string::npos || m_column.m_width > m_column.m_initialIndent);
+			calcLength();
+			if (m_len == 0)
+				m_stringIndex++; // Empty string
+		}
+
+		auto operator *() const -> std::string {
+			assert(m_stringIndex < m_column.m_strings.size());
+			assert(m_pos <= m_end);
+			return addIndentAndSuffix(line().substr(m_pos, m_len));
+		}
+
+		auto operator ++() -> iterator& {
+			m_pos += m_len;
+			if (m_pos < line().size() && line()[m_pos] == '\n')
+				m_pos += 1;
+			else
+				while (m_pos < line().size() && isWhitespace(line()[m_pos]))
+					++m_pos;
+
+			if (m_pos == line().size()) {
+				m_pos = 0;
+				++m_stringIndex;
+			}
+			if (m_stringIndex < m_column.m_strings.size())
+				calcLength();
+			return *this;
+		}
+		auto operator ++(int) -> iterator {
+			iterator prev(*this);
+			operator++();
+			return prev;
+		}
+
+		auto operator ==(iterator const& other) const -> bool {
+			return
+				m_pos == other.m_pos &&
+				m_stringIndex == other.m_stringIndex &&
+				&m_column == &other.m_column;
+		}
+		auto operator !=(iterator const& other) const -> bool {
+			return !operator==(other);
+		}
+	};
+	using const_iterator = iterator;
+
+	explicit Column(std::string const& text) { m_strings.push_back(text); }
+
+	auto width(size_t newWidth) -> Column& {
+		assert(newWidth > 0);
+		m_width = newWidth;
+		return *this;
+	}
+	auto indent(size_t newIndent) -> Column& {
+		m_indent = newIndent;
+		return *this;
+	}
+	auto initialIndent(size_t newIndent) -> Column& {
+		m_initialIndent = newIndent;
+		return *this;
+	}
+
+	auto width() const -> size_t { return m_width; }
+	auto begin() const -> iterator { return iterator(*this); }
+	auto end() const -> iterator { return { *this, m_strings.size() }; }
+
+	inline friend std::ostream& operator << (std::ostream& os, Column const& col) {
+		bool first = true;
+		for (auto line : col) {
+			if (first)
+				first = false;
+			else
+				os << "\n";
+			os << line;
+		}
+		return os;
+	}
+
+	auto operator + (Column const& other)->Columns;
+
+	auto toString() const -> std::string {
+		std::ostringstream oss;
+		oss << *this;
+		return oss.str();
+	}
+};
+
+class Spacer : public Column {
+
+public:
+	explicit Spacer(size_t spaceWidth) : Column("") {
+		width(spaceWidth);
+	}
+};
+
+class Columns {
+	std::vector<Column> m_columns;
+
+public:
+
+	class iterator {
+		friend Columns;
+		struct EndTag {};
+
+		std::vector<Column> const& m_columns;
+		std::vector<Column::iterator> m_iterators;
+		size_t m_activeIterators;
+
+		iterator(Columns const& columns, EndTag)
+			: m_columns(columns.m_columns),
+			m_activeIterators(0) {
+			m_iterators.reserve(m_columns.size());
+
+			for (auto const& col : m_columns)
+				m_iterators.push_back(col.end());
+		}
+
+	public:
+		using difference_type = std::ptrdiff_t;
+		using value_type = std::string;
+		using pointer = value_type * ;
+		using reference = value_type & ;
+		using iterator_category = std::forward_iterator_tag;
+
+		explicit iterator(Columns const& columns)
+			: m_columns(columns.m_columns),
+			m_activeIterators(m_columns.size()) {
+			m_iterators.reserve(m_columns.size());
+
+			for (auto const& col : m_columns)
+				m_iterators.push_back(col.begin());
+		}
+
+		auto operator ==(iterator const& other) const -> bool {
+			return m_iterators == other.m_iterators;
+		}
+		auto operator !=(iterator const& other) const -> bool {
+			return m_iterators != other.m_iterators;
+		}
+		auto operator *() const -> std::string {
+			std::string row, padding;
+
+			for (size_t i = 0; i < m_columns.size(); ++i) {
+				auto width = m_columns[i].width();
+				if (m_iterators[i] != m_columns[i].end()) {
+					std::string col = *m_iterators[i];
+					row += padding + col;
+					if (col.size() < width)
+						padding = std::string(width - col.size(), ' ');
+					else
+						padding = "";
+				} else {
+					padding += std::string(width, ' ');
+				}
+			}
+			return row;
+		}
+		auto operator ++() -> iterator& {
+			for (size_t i = 0; i < m_columns.size(); ++i) {
+				if (m_iterators[i] != m_columns[i].end())
+					++m_iterators[i];
+			}
+			return *this;
+		}
+		auto operator ++(int) -> iterator {
+			iterator prev(*this);
+			operator++();
+			return prev;
+		}
+	};
+	using const_iterator = iterator;
+
+	auto begin() const -> iterator { return iterator(*this); }
+	auto end() const -> iterator { return { *this, iterator::EndTag() }; }
+
+	auto operator += (Column const& col) -> Columns& {
+		m_columns.push_back(col);
+		return *this;
+	}
+	auto operator + (Column const& col) -> Columns {
+		Columns combined = *this;
+		combined += col;
+		return combined;
+	}
+
+	inline friend std::ostream& operator << (std::ostream& os, Columns const& cols) {
+
+		bool first = true;
+		for (auto line : cols) {
+			if (first)
+				first = false;
+			else
+				os << "\n";
+			os << line;
+		}
+		return os;
+	}
+
+	auto toString() const -> std::string {
+		std::ostringstream oss;
+		oss << *this;
+		return oss.str();
+	}
+};
+
+inline auto Column::operator + (Column const& other) -> Columns {
+	Columns cols;
+	cols += *this;
+	cols += other;
+	return cols;
+}
+}
+
+}
+}
+
+// ----------- end of #include from clara_textflow.hpp -----------
+// ........... back in clara.hpp
+
+#include <cctype>
+#include <string>
+#include <memory>
+#include <set>
+#include <algorithm>
+
+#if !defined(CATCH_PLATFORM_WINDOWS) && ( defined(WIN32) || defined(__WIN32__) || defined(_WIN32) || defined(_MSC_VER) )
+#define CATCH_PLATFORM_WINDOWS
+#endif
+
+namespace Catch { namespace clara {
+namespace detail {
+
+    // Traits for extracting arg and return type of lambdas (for single argument lambdas)
+    template<typename L>
+    struct UnaryLambdaTraits : UnaryLambdaTraits<decltype( &L::operator() )> {};
+
+    template<typename ClassT, typename ReturnT, typename... Args>
+    struct UnaryLambdaTraits<ReturnT( ClassT::* )( Args... ) const> {
+        static const bool isValid = false;
+    };
+
+    template<typename ClassT, typename ReturnT, typename ArgT>
+    struct UnaryLambdaTraits<ReturnT( ClassT::* )( ArgT ) const> {
+        static const bool isValid = true;
+        using ArgType = typename std::remove_const<typename std::remove_reference<ArgT>::type>::type;
+        using ReturnType = ReturnT;
+    };
+
+    class TokenStream;
+
+    // Transport for raw args (copied from main args, or supplied via init list for testing)
+    class Args {
+        friend TokenStream;
+        std::string m_exeName;
+        std::vector<std::string> m_args;
+
+    public:
+        Args( int argc, char const* const* argv )
+            : m_exeName(argv[0]),
+              m_args(argv + 1, argv + argc) {}
+
+        Args( std::initializer_list<std::string> args )
+        :   m_exeName( *args.begin() ),
+            m_args( args.begin()+1, args.end() )
+        {}
+
+        auto exeName() const -> std::string {
+            return m_exeName;
+        }
+    };
+
+    // Wraps a token coming from a token stream. These may not directly correspond to strings as a single string
+    // may encode an option + its argument if the : or = form is used
+    enum class TokenType {
+        Option, Argument
+    };
+    struct Token {
+        TokenType type;
+        std::string token;
+    };
+
+    inline auto isOptPrefix( char c ) -> bool {
+        return c == '-'
+#ifdef CATCH_PLATFORM_WINDOWS
+            || c == '/'
+#endif
+        ;
+    }
+
+    // Abstracts iterators into args as a stream of tokens, with option arguments uniformly handled
+    class TokenStream {
+        using Iterator = std::vector<std::string>::const_iterator;
+        Iterator it;
+        Iterator itEnd;
+        std::vector<Token> m_tokenBuffer;
+
+        void loadBuffer() {
+            m_tokenBuffer.resize( 0 );
+
+            // Skip any empty strings
+            while( it != itEnd && it->empty() )
+                ++it;
+
+            if( it != itEnd ) {
+                auto const &next = *it;
+                if( isOptPrefix( next[0] ) ) {
+                    auto delimiterPos = next.find_first_of( " :=" );
+                    if( delimiterPos != std::string::npos ) {
+                        m_tokenBuffer.push_back( { TokenType::Option, next.substr( 0, delimiterPos ) } );
+                        m_tokenBuffer.push_back( { TokenType::Argument, next.substr( delimiterPos + 1 ) } );
+                    } else {
+                        if( next[1] != '-' && next.size() > 2 ) {
+                            std::string opt = "- ";
+                            for( size_t i = 1; i < next.size(); ++i ) {
+                                opt[1] = next[i];
+                                m_tokenBuffer.push_back( { TokenType::Option, opt } );
+                            }
+                        } else {
+                            m_tokenBuffer.push_back( { TokenType::Option, next } );
+                        }
+                    }
+                } else {
+                    m_tokenBuffer.push_back( { TokenType::Argument, next } );
+                }
+            }
+        }
+
+    public:
+        explicit TokenStream( Args const &args ) : TokenStream( args.m_args.begin(), args.m_args.end() ) {}
+
+        TokenStream( Iterator it, Iterator itEnd ) : it( it ), itEnd( itEnd ) {
+            loadBuffer();
+        }
+
+        explicit operator bool() const {
+            return !m_tokenBuffer.empty() || it != itEnd;
+        }
+
+        auto count() const -> size_t { return m_tokenBuffer.size() + (itEnd - it); }
+
+        auto operator*() const -> Token {
+            assert( !m_tokenBuffer.empty() );
+            return m_tokenBuffer.front();
+        }
+
+        auto operator->() const -> Token const * {
+            assert( !m_tokenBuffer.empty() );
+            return &m_tokenBuffer.front();
+        }
+
+        auto operator++() -> TokenStream & {
+            if( m_tokenBuffer.size() >= 2 ) {
+                m_tokenBuffer.erase( m_tokenBuffer.begin() );
+            } else {
+                if( it != itEnd )
+                    ++it;
+                loadBuffer();
+            }
+            return *this;
+        }
+    };
+
+    class ResultBase {
+    public:
+        enum Type {
+            Ok, LogicError, RuntimeError
+        };
+
+    protected:
+        ResultBase( Type type ) : m_type( type ) {}
+        virtual ~ResultBase() = default;
+
+        virtual void enforceOk() const = 0;
+
+        Type m_type;
+    };
+
+    template<typename T>
+    class ResultValueBase : public ResultBase {
+    public:
+        auto value() const -> T const & {
+            enforceOk();
+            return m_value;
+        }
+
+    protected:
+        ResultValueBase( Type type ) : ResultBase( type ) {}
+
+        ResultValueBase( ResultValueBase const &other ) : ResultBase( other ) {
+            if( m_type == ResultBase::Ok )
+                new( &m_value ) T( other.m_value );
+        }
+
+        ResultValueBase( Type, T const &value ) : ResultBase( Ok ) {
+            new( &m_value ) T( value );
+        }
+
+        auto operator=( ResultValueBase const &other ) -> ResultValueBase & {
+            if( m_type == ResultBase::Ok )
+                m_value.~T();
+            ResultBase::operator=(other);
+            if( m_type == ResultBase::Ok )
+                new( &m_value ) T( other.m_value );
+            return *this;
+        }
+
+        ~ResultValueBase() override {
+            if( m_type == Ok )
+                m_value.~T();
+        }
+
+        union {
+            T m_value;
+        };
+    };
+
+    template<>
+    class ResultValueBase<void> : public ResultBase {
+    protected:
+        using ResultBase::ResultBase;
+    };
+
+    template<typename T = void>
+    class BasicResult : public ResultValueBase<T> {
+    public:
+        template<typename U>
+        explicit BasicResult( BasicResult<U> const &other )
+        :   ResultValueBase<T>( other.type() ),
+            m_errorMessage( other.errorMessage() )
+        {
+            assert( type() != ResultBase::Ok );
+        }
+
+        template<typename U>
+        static auto ok( U const &value ) -> BasicResult { return { ResultBase::Ok, value }; }
+        static auto ok() -> BasicResult { return { ResultBase::Ok }; }
+        static auto logicError( std::string const &message ) -> BasicResult { return { ResultBase::LogicError, message }; }
+        static auto runtimeError( std::string const &message ) -> BasicResult { return { ResultBase::RuntimeError, message }; }
+
+        explicit operator bool() const { return m_type == ResultBase::Ok; }
+        auto type() const -> ResultBase::Type { return m_type; }
+        auto errorMessage() const -> std::string { return m_errorMessage; }
+
+    protected:
+        void enforceOk() const override {
+
+            // Errors shouldn't reach this point, but if they do
+            // the actual error message will be in m_errorMessage
+            assert( m_type != ResultBase::LogicError );
+            assert( m_type != ResultBase::RuntimeError );
+            if( m_type != ResultBase::Ok )
+                std::abort();
+        }
+
+        std::string m_errorMessage; // Only populated if resultType is an error
+
+        BasicResult( ResultBase::Type type, std::string const &message )
+        :   ResultValueBase<T>(type),
+            m_errorMessage(message)
+        {
+            assert( m_type != ResultBase::Ok );
+        }
+
+        using ResultValueBase<T>::ResultValueBase;
+        using ResultBase::m_type;
+    };
+
+    enum class ParseResultType {
+        Matched, NoMatch, ShortCircuitAll, ShortCircuitSame
+    };
+
+    class ParseState {
+    public:
+
+        ParseState( ParseResultType type, TokenStream const &remainingTokens )
+        : m_type(type),
+          m_remainingTokens( remainingTokens )
+        {}
+
+        auto type() const -> ParseResultType { return m_type; }
+        auto remainingTokens() const -> TokenStream { return m_remainingTokens; }
+
+    private:
+        ParseResultType m_type;
+        TokenStream m_remainingTokens;
+    };
+
+    using Result = BasicResult<void>;
+    using ParserResult = BasicResult<ParseResultType>;
+    using InternalParseResult = BasicResult<ParseState>;
+
+    struct HelpColumns {
+        std::string left;
+        std::string right;
+    };
+
+    template<typename T>
+    inline auto convertInto( std::string const &source, T& target ) -> ParserResult {
+        std::stringstream ss;
+        ss << source;
+        ss >> target;
+        if( ss.fail() )
+            return ParserResult::runtimeError( "Unable to convert '" + source + "' to destination type" );
+        else
+            return ParserResult::ok( ParseResultType::Matched );
+    }
+    inline auto convertInto( std::string const &source, std::string& target ) -> ParserResult {
+        target = source;
+        return ParserResult::ok( ParseResultType::Matched );
+    }
+    inline auto convertInto( std::string const &source, bool &target ) -> ParserResult {
+        std::string srcLC = source;
+        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")
+            target = false;
+        else
+            return ParserResult::runtimeError( "Expected a boolean value but did not recognise: '" + source + "'" );
+        return ParserResult::ok( ParseResultType::Matched );
+    }
+#ifdef CLARA_CONFIG_OPTIONAL_TYPE
+    template<typename T>
+    inline auto convertInto( std::string const &source, CLARA_CONFIG_OPTIONAL_TYPE<T>& target ) -> ParserResult {
+        T temp;
+        auto result = convertInto( source, temp );
+        if( result )
+            target = std::move(temp);
+        return result;
+    }
+#endif // CLARA_CONFIG_OPTIONAL_TYPE
+
+    struct NonCopyable {
+        NonCopyable() = default;
+        NonCopyable( NonCopyable const & ) = delete;
+        NonCopyable( NonCopyable && ) = delete;
+        NonCopyable &operator=( NonCopyable const & ) = delete;
+        NonCopyable &operator=( NonCopyable && ) = delete;
+    };
+
+    struct BoundRef : NonCopyable {
+        virtual ~BoundRef() = default;
+        virtual auto isContainer() const -> bool { return false; }
+        virtual auto isFlag() const -> bool { return false; }
+    };
+    struct BoundValueRefBase : BoundRef {
+        virtual auto setValue( std::string const &arg ) -> ParserResult = 0;
+    };
+    struct BoundFlagRefBase : BoundRef {
+        virtual auto setFlag( bool flag ) -> ParserResult = 0;
+        virtual auto isFlag() const -> bool { return true; }
+    };
+
+    template<typename T>
+    struct BoundValueRef : BoundValueRefBase {
+        T &m_ref;
+
+        explicit BoundValueRef( T &ref ) : m_ref( ref ) {}
+
+        auto setValue( std::string const &arg ) -> ParserResult override {
+            return convertInto( arg, m_ref );
+        }
+    };
+
+    template<typename T>
+    struct BoundValueRef<std::vector<T>> : BoundValueRefBase {
+        std::vector<T> &m_ref;
+
+        explicit BoundValueRef( std::vector<T> &ref ) : m_ref( ref ) {}
+
+        auto isContainer() const -> bool override { return true; }
+
+        auto setValue( std::string const &arg ) -> ParserResult override {
+            T temp;
+            auto result = convertInto( arg, temp );
+            if( result )
+                m_ref.push_back( temp );
+            return result;
+        }
+    };
+
+    struct BoundFlagRef : BoundFlagRefBase {
+        bool &m_ref;
+
+        explicit BoundFlagRef( bool &ref ) : m_ref( ref ) {}
+
+        auto setFlag( bool flag ) -> ParserResult override {
+            m_ref = flag;
+            return ParserResult::ok( ParseResultType::Matched );
+        }
+    };
+
+    template<typename ReturnType>
+    struct LambdaInvoker {
+        static_assert( std::is_same<ReturnType, ParserResult>::value, "Lambda must return void or clara::ParserResult" );
+
+        template<typename L, typename ArgType>
+        static auto invoke( L const &lambda, ArgType const &arg ) -> ParserResult {
+            return lambda( arg );
+        }
+    };
+
+    template<>
+    struct LambdaInvoker<void> {
+        template<typename L, typename ArgType>
+        static auto invoke( L const &lambda, ArgType const &arg ) -> ParserResult {
+            lambda( arg );
+            return ParserResult::ok( ParseResultType::Matched );
+        }
+    };
+
+    template<typename ArgType, typename L>
+    inline auto invokeLambda( L const &lambda, std::string const &arg ) -> ParserResult {
+        ArgType temp{};
+        auto result = convertInto( arg, temp );
+        return !result
+           ? result
+           : LambdaInvoker<typename UnaryLambdaTraits<L>::ReturnType>::invoke( lambda, temp );
+    }
+
+    template<typename L>
+    struct BoundLambda : BoundValueRefBase {
+        L m_lambda;
+
+        static_assert( UnaryLambdaTraits<L>::isValid, "Supplied lambda must take exactly one argument" );
+        explicit BoundLambda( L const &lambda ) : m_lambda( lambda ) {}
+
+        auto setValue( std::string const &arg ) -> ParserResult override {
+            return invokeLambda<typename UnaryLambdaTraits<L>::ArgType>( m_lambda, arg );
+        }
+    };
+
+    template<typename L>
+    struct BoundFlagLambda : BoundFlagRefBase {
+        L m_lambda;
+
+        static_assert( UnaryLambdaTraits<L>::isValid, "Supplied lambda must take exactly one argument" );
+        static_assert( std::is_same<typename UnaryLambdaTraits<L>::ArgType, bool>::value, "flags must be boolean" );
+
+        explicit BoundFlagLambda( L const &lambda ) : m_lambda( lambda ) {}
+
+        auto setFlag( bool flag ) -> ParserResult override {
+            return LambdaInvoker<typename UnaryLambdaTraits<L>::ReturnType>::invoke( m_lambda, flag );
+        }
+    };
+
+    enum class Optionality { Optional, Required };
+
+    struct Parser;
+
+    class ParserBase {
+    public:
+        virtual ~ParserBase() = default;
+        virtual auto validate() const -> Result { return Result::ok(); }
+        virtual auto parse( std::string const& exeName, TokenStream const &tokens) const -> InternalParseResult  = 0;
+        virtual auto cardinality() const -> size_t { return 1; }
+
+        auto parse( Args const &args ) const -> InternalParseResult {
+            return parse( args.exeName(), TokenStream( args ) );
+        }
+    };
+
+    template<typename DerivedT>
+    class ComposableParserImpl : public ParserBase {
+    public:
+        template<typename T>
+        auto operator|( T const &other ) const -> Parser;
+
+		template<typename T>
+        auto operator+( T const &other ) const -> Parser;
+    };
+
+    // Common code and state for Args and Opts
+    template<typename DerivedT>
+    class ParserRefImpl : public ComposableParserImpl<DerivedT> {
+    protected:
+        Optionality m_optionality = Optionality::Optional;
+        std::shared_ptr<BoundRef> m_ref;
+        std::string m_hint;
+        std::string m_description;
+
+        explicit ParserRefImpl( std::shared_ptr<BoundRef> const &ref ) : m_ref( ref ) {}
+
+    public:
+        template<typename T>
+        ParserRefImpl( T &ref, std::string const &hint )
+        :   m_ref( std::make_shared<BoundValueRef<T>>( ref ) ),
+            m_hint( hint )
+        {}
+
+        template<typename LambdaT>
+        ParserRefImpl( LambdaT const &ref, std::string const &hint )
+        :   m_ref( std::make_shared<BoundLambda<LambdaT>>( ref ) ),
+            m_hint(hint)
+        {}
+
+        auto operator()( std::string const &description ) -> DerivedT & {
+            m_description = description;
+            return static_cast<DerivedT &>( *this );
+        }
+
+        auto optional() -> DerivedT & {
+            m_optionality = Optionality::Optional;
+            return static_cast<DerivedT &>( *this );
+        };
+
+        auto required() -> DerivedT & {
+            m_optionality = Optionality::Required;
+            return static_cast<DerivedT &>( *this );
+        };
+
+        auto isOptional() const -> bool {
+            return m_optionality == Optionality::Optional;
+        }
+
+        auto cardinality() const -> size_t override {
+            if( m_ref->isContainer() )
+                return 0;
+            else
+                return 1;
+        }
+
+        auto hint() const -> std::string { return m_hint; }
+    };
+
+    class ExeName : public ComposableParserImpl<ExeName> {
+        std::shared_ptr<std::string> m_name;
+        std::shared_ptr<BoundValueRefBase> m_ref;
+
+        template<typename LambdaT>
+        static auto makeRef(LambdaT const &lambda) -> std::shared_ptr<BoundValueRefBase> {
+            return std::make_shared<BoundLambda<LambdaT>>( lambda) ;
+        }
+
+    public:
+        ExeName() : m_name( std::make_shared<std::string>( "<executable>" ) ) {}
+
+        explicit ExeName( std::string &ref ) : ExeName() {
+            m_ref = std::make_shared<BoundValueRef<std::string>>( ref );
+        }
+
+        template<typename LambdaT>
+        explicit ExeName( LambdaT const& lambda ) : ExeName() {
+            m_ref = std::make_shared<BoundLambda<LambdaT>>( lambda );
+        }
+
+        // The exe name is not parsed out of the normal tokens, but is handled specially
+        auto parse( std::string const&, TokenStream const &tokens ) const -> InternalParseResult override {
+            return InternalParseResult::ok( ParseState( ParseResultType::NoMatch, tokens ) );
+        }
+
+        auto name() const -> std::string { return *m_name; }
+        auto set( std::string const& newName ) -> ParserResult {
+
+            auto lastSlash = newName.find_last_of( "\\/" );
+            auto filename = ( lastSlash == std::string::npos )
+                    ? newName
+                    : newName.substr( lastSlash+1 );
+
+            *m_name = filename;
+            if( m_ref )
+                return m_ref->setValue( filename );
+            else
+                return ParserResult::ok( ParseResultType::Matched );
+        }
+    };
+
+    class Arg : public ParserRefImpl<Arg> {
+    public:
+        using ParserRefImpl::ParserRefImpl;
+
+        auto parse( std::string const &, TokenStream const &tokens ) const -> InternalParseResult override {
+            auto validationResult = validate();
+            if( !validationResult )
+                return InternalParseResult( validationResult );
+
+            auto remainingTokens = tokens;
+            auto const &token = *remainingTokens;
+            if( token.type != TokenType::Argument )
+                return InternalParseResult::ok( ParseState( ParseResultType::NoMatch, remainingTokens ) );
+
+            assert( !m_ref->isFlag() );
+            auto valueRef = static_cast<detail::BoundValueRefBase*>( m_ref.get() );
+
+            auto result = valueRef->setValue( remainingTokens->token );
+            if( !result )
+                return InternalParseResult( result );
+            else
+                return InternalParseResult::ok( ParseState( ParseResultType::Matched, ++remainingTokens ) );
+        }
+    };
+
+    inline auto normaliseOpt( std::string const &optName ) -> std::string {
+#ifdef CATCH_PLATFORM_WINDOWS
+        if( optName[0] == '/' )
+            return "-" + optName.substr( 1 );
+        else
+#endif
+            return optName;
+    }
+
+    class Opt : public ParserRefImpl<Opt> {
+    protected:
+        std::vector<std::string> m_optNames;
+
+    public:
+        template<typename LambdaT>
+        explicit Opt( LambdaT const &ref ) : ParserRefImpl( std::make_shared<BoundFlagLambda<LambdaT>>( ref ) ) {}
+
+        explicit Opt( bool &ref ) : ParserRefImpl( std::make_shared<BoundFlagRef>( ref ) ) {}
+
+        template<typename LambdaT>
+        Opt( LambdaT const &ref, std::string const &hint ) : ParserRefImpl( ref, hint ) {}
+
+        template<typename T>
+        Opt( T &ref, std::string const &hint ) : ParserRefImpl( ref, hint ) {}
+
+        auto operator[]( std::string const &optName ) -> Opt & {
+            m_optNames.push_back( optName );
+            return *this;
+        }
+
+        auto getHelpColumns() const -> std::vector<HelpColumns> {
+            std::ostringstream oss;
+            bool first = true;
+            for( auto const &opt : m_optNames ) {
+                if (first)
+                    first = false;
+                else
+                    oss << ", ";
+                oss << opt;
+            }
+            if( !m_hint.empty() )
+                oss << " <" << m_hint << ">";
+            return { { oss.str(), m_description } };
+        }
+
+        auto isMatch( std::string const &optToken ) const -> bool {
+            auto normalisedToken = normaliseOpt( optToken );
+            for( auto const &name : m_optNames ) {
+                if( normaliseOpt( name ) == normalisedToken )
+                    return true;
+            }
+            return false;
+        }
+
+        using ParserBase::parse;
+
+        auto parse( std::string const&, TokenStream const &tokens ) const -> InternalParseResult override {
+            auto validationResult = validate();
+            if( !validationResult )
+                return InternalParseResult( validationResult );
+
+            auto remainingTokens = tokens;
+            if( remainingTokens && remainingTokens->type == TokenType::Option ) {
+                auto const &token = *remainingTokens;
+                if( isMatch(token.token ) ) {
+                    if( m_ref->isFlag() ) {
+                        auto flagRef = static_cast<detail::BoundFlagRefBase*>( m_ref.get() );
+                        auto result = flagRef->setFlag( true );
+                        if( !result )
+                            return InternalParseResult( result );
+                        if( result.value() == ParseResultType::ShortCircuitAll )
+                            return InternalParseResult::ok( ParseState( result.value(), remainingTokens ) );
+                    } else {
+                        auto valueRef = static_cast<detail::BoundValueRefBase*>( m_ref.get() );
+                        ++remainingTokens;
+                        if( !remainingTokens )
+                            return InternalParseResult::runtimeError( "Expected argument following " + token.token );
+                        auto const &argToken = *remainingTokens;
+                        if( argToken.type != TokenType::Argument )
+                            return InternalParseResult::runtimeError( "Expected argument following " + token.token );
+                        auto result = valueRef->setValue( argToken.token );
+                        if( !result )
+                            return InternalParseResult( result );
+                        if( result.value() == ParseResultType::ShortCircuitAll )
+                            return InternalParseResult::ok( ParseState( result.value(), remainingTokens ) );
+                    }
+                    return InternalParseResult::ok( ParseState( ParseResultType::Matched, ++remainingTokens ) );
+                }
+            }
+            return InternalParseResult::ok( ParseState( ParseResultType::NoMatch, remainingTokens ) );
+        }
+
+        auto validate() const -> Result override {
+            if( m_optNames.empty() )
+                return Result::logicError( "No options supplied to Opt" );
+            for( auto const &name : m_optNames ) {
+                if( name.empty() )
+                    return Result::logicError( "Option name cannot be empty" );
+#ifdef CATCH_PLATFORM_WINDOWS
+                if( name[0] != '-' && name[0] != '/' )
+                    return Result::logicError( "Option name must begin with '-' or '/'" );
+#else
+                if( name[0] != '-' )
+                    return Result::logicError( "Option name must begin with '-'" );
+#endif
+            }
+            return ParserRefImpl::validate();
+        }
+    };
+
+    struct Help : Opt {
+        Help( bool &showHelpFlag )
+        :   Opt([&]( bool flag ) {
+                showHelpFlag = flag;
+                return ParserResult::ok( ParseResultType::ShortCircuitAll );
+            })
+        {
+            static_cast<Opt &>( *this )
+                    ("display usage information")
+                    ["-?"]["-h"]["--help"]
+                    .optional();
+        }
+    };
+
+    struct Parser : ParserBase {
+
+        mutable ExeName m_exeName;
+        std::vector<Opt> m_options;
+        std::vector<Arg> m_args;
+
+        auto operator|=( ExeName const &exeName ) -> Parser & {
+            m_exeName = exeName;
+            return *this;
+        }
+
+        auto operator|=( Arg const &arg ) -> Parser & {
+            m_args.push_back(arg);
+            return *this;
+        }
+
+        auto operator|=( Opt const &opt ) -> Parser & {
+            m_options.push_back(opt);
+            return *this;
+        }
+
+        auto operator|=( Parser const &other ) -> Parser & {
+            m_options.insert(m_options.end(), other.m_options.begin(), other.m_options.end());
+            m_args.insert(m_args.end(), other.m_args.begin(), other.m_args.end());
+            return *this;
+        }
+
+        template<typename T>
+        auto operator|( T const &other ) const -> Parser {
+            return Parser( *this ) |= other;
+        }
+
+        // Forward deprecated interface with '+' instead of '|'
+        template<typename T>
+        auto operator+=( T const &other ) -> Parser & { return operator|=( other ); }
+        template<typename T>
+        auto operator+( T const &other ) const -> Parser { return operator|( other ); }
+
+        auto getHelpColumns() const -> std::vector<HelpColumns> {
+            std::vector<HelpColumns> cols;
+            for (auto const &o : m_options) {
+                auto childCols = o.getHelpColumns();
+                cols.insert( cols.end(), childCols.begin(), childCols.end() );
+            }
+            return cols;
+        }
+
+        void writeToStream( std::ostream &os ) const {
+            if (!m_exeName.name().empty()) {
+                os << "usage:\n" << "  " << m_exeName.name() << " ";
+                bool required = true, first = true;
+                for( auto const &arg : m_args ) {
+                    if (first)
+                        first = false;
+                    else
+                        os << " ";
+                    if( arg.isOptional() && required ) {
+                        os << "[";
+                        required = false;
+                    }
+                    os << "<" << arg.hint() << ">";
+                    if( arg.cardinality() == 0 )
+                        os << " ... ";
+                }
+                if( !required )
+                    os << "]";
+                if( !m_options.empty() )
+                    os << " options";
+                os << "\n\nwhere options are:" << std::endl;
+            }
+
+            auto rows = getHelpColumns();
+            size_t consoleWidth = CATCH_CLARA_CONFIG_CONSOLE_WIDTH;
+            size_t optWidth = 0;
+            for( auto const &cols : rows )
+                optWidth = (std::max)(optWidth, cols.left.size() + 2);
+
+            optWidth = (std::min)(optWidth, consoleWidth/2);
+
+            for( auto const &cols : rows ) {
+                auto row =
+                        TextFlow::Column( cols.left ).width( optWidth ).indent( 2 ) +
+                        TextFlow::Spacer(4) +
+                        TextFlow::Column( cols.right ).width( consoleWidth - 7 - optWidth );
+                os << row << std::endl;
+            }
+        }
+
+        friend auto operator<<( std::ostream &os, Parser const &parser ) -> std::ostream& {
+            parser.writeToStream( os );
+            return os;
+        }
+
+        auto validate() const -> Result override {
+            for( auto const &opt : m_options ) {
+                auto result = opt.validate();
+                if( !result )
+                    return result;
+            }
+            for( auto const &arg : m_args ) {
+                auto result = arg.validate();
+                if( !result )
+                    return result;
+            }
+            return Result::ok();
+        }
+
+        using ParserBase::parse;
+
+        auto parse( std::string const& exeName, TokenStream const &tokens ) const -> InternalParseResult override {
+
+            struct ParserInfo {
+                ParserBase const* parser = nullptr;
+                size_t count = 0;
+            };
+            const size_t totalParsers = m_options.size() + m_args.size();
+            assert( totalParsers < 512 );
+            // ParserInfo parseInfos[totalParsers]; // <-- this is what we really want to do
+            ParserInfo parseInfos[512];
+
+            {
+                size_t i = 0;
+                for (auto const &opt : m_options) parseInfos[i++].parser = &opt;
+                for (auto const &arg : m_args) parseInfos[i++].parser = &arg;
+            }
+
+            m_exeName.set( exeName );
+
+            auto result = InternalParseResult::ok( ParseState( ParseResultType::NoMatch, tokens ) );
+            while( result.value().remainingTokens() ) {
+                bool tokenParsed = false;
+
+                for( size_t i = 0; i < totalParsers; ++i ) {
+                    auto&  parseInfo = parseInfos[i];
+                    if( parseInfo.parser->cardinality() == 0 || parseInfo.count < parseInfo.parser->cardinality() ) {
+                        result = parseInfo.parser->parse(exeName, result.value().remainingTokens());
+                        if (!result)
+                            return result;
+                        if (result.value().type() != ParseResultType::NoMatch) {
+                            tokenParsed = true;
+                            ++parseInfo.count;
+                            break;
+                        }
+                    }
+                }
+
+                if( result.value().type() == ParseResultType::ShortCircuitAll )
+                    return result;
+                if( !tokenParsed )
+                    return InternalParseResult::runtimeError( "Unrecognised token: " + result.value().remainingTokens()->token );
+            }
+            // !TBD Check missing required options
+            return result;
+        }
+    };
+
+    template<typename DerivedT>
+    template<typename T>
+    auto ComposableParserImpl<DerivedT>::operator|( T const &other ) const -> Parser {
+        return Parser() | static_cast<DerivedT const &>( *this ) | other;
+    }
+} // namespace detail
+
+// A Combined parser
+using detail::Parser;
+
+// A parser for options
+using detail::Opt;
+
+// A parser for arguments
+using detail::Arg;
+
+// Wrapper for argc, argv from main()
+using detail::Args;
+
+// Specifies the name of the executable
+using detail::ExeName;
+
+// Convenience wrapper for option parser that specifies the help option
+using detail::Help;
+
+// enum of result types from a parse
+using detail::ParseResultType;
+
+// Result type for parser operation
+using detail::ParserResult;
+
+}} // namespace Catch::clara
+
+// end clara.hpp
+#ifdef __clang__
+#pragma clang diagnostic pop
+#endif
+
+// Restore Clara's value for console width, if present
+#ifdef CATCH_TEMP_CLARA_CONFIG_CONSOLE_WIDTH
+#define CATCH_CLARA_TEXTFLOW_CONFIG_CONSOLE_WIDTH CATCH_TEMP_CLARA_CONFIG_CONSOLE_WIDTH
+#undef CATCH_TEMP_CLARA_CONFIG_CONSOLE_WIDTH
+#endif
+
+// end catch_clara.h
+namespace Catch {
+
+    clara::Parser makeCommandLineParser( ConfigData& config );
+
+} // end namespace Catch
+
+// end catch_commandline.h
+#include <fstream>
+#include <ctime>
+
+namespace Catch {
+
+    clara::Parser makeCommandLineParser( ConfigData& config ) {
+
+        using namespace clara;
+
+        auto const setWarning = [&]( std::string const& warning ) {
+                auto warningSet = [&]() {
+                    if( warning == "NoAssertions" )
+                        return WarnAbout::NoAssertions;
+
+                    if ( warning == "NoTests" )
+                        return WarnAbout::NoTests;
+
+                    return WarnAbout::Nothing;
+                }();
+
+                if (warningSet == WarnAbout::Nothing)
+                    return ParserResult::runtimeError( "Unrecognised warning: '" + warning + "'" );
+                config.warnings = static_cast<WarnAbout::What>( config.warnings | warningSet );
+                return ParserResult::ok( ParseResultType::Matched );
+            };
+        auto const loadTestNamesFromFile = [&]( std::string const& filename ) {
+                std::ifstream f( filename.c_str() );
+                if( !f.is_open() )
+                    return ParserResult::runtimeError( "Unable to load input file: '" + filename + "'" );
+
+                std::string line;
+                while( std::getline( f, line ) ) {
+                    line = trim(line);
+                    if( !line.empty() && !startsWith( line, '#' ) ) {
+                        if( !startsWith( line, '"' ) )
+                            line = '"' + 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 ) {
+                if( startsWith( "declared", order ) )
+                    config.runOrder = RunTests::InDeclarationOrder;
+                else if( startsWith( "lexical", order ) )
+                    config.runOrder = RunTests::InLexicographicalOrder;
+                else if( startsWith( "random", order ) )
+                    config.runOrder = RunTests::InRandomOrder;
+                else
+                    return clara::ParserResult::runtimeError( "Unrecognised ordering: '" + order + "'" );
+                return ParserResult::ok( ParseResultType::Matched );
+            };
+        auto const setRngSeed = [&]( std::string const& seed ) {
+                if( seed != "time" )
+                    return clara::detail::convertInto( seed, config.rngSeed );
+                config.rngSeed = static_cast<unsigned int>( std::time(nullptr) );
+                return ParserResult::ok( ParseResultType::Matched );
+            };
+        auto const setColourUsage = [&]( std::string const& useColour ) {
+                    auto mode = toLower( useColour );
+
+                    if( mode == "yes" )
+                        config.useColour = UseColour::Yes;
+                    else if( mode == "no" )
+                        config.useColour = UseColour::No;
+                    else if( mode == "auto" )
+                        config.useColour = UseColour::Auto;
+                    else
+                        return ParserResult::runtimeError( "colour mode must be one of: auto, yes or no. '" + useColour + "' not recognised" );
+                return ParserResult::ok( ParseResultType::Matched );
+            };
+        auto const setWaitForKeypress = [&]( std::string const& keypress ) {
+                auto keypressLc = toLower( keypress );
+                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: never, start, exit or both. '" + keypress + "' not recognised" );
+            return ParserResult::ok( ParseResultType::Matched );
+            };
+        auto const setVerbosity = [&]( std::string const& verbosity ) {
+            auto lcVerbosity = toLower( verbosity );
+            if( lcVerbosity == "quiet" )
+                config.verbosity = Verbosity::Quiet;
+            else if( lcVerbosity == "normal" )
+                config.verbosity = Verbosity::Normal;
+            else if( lcVerbosity == "high" )
+                config.verbosity = Verbosity::High;
+            else
+                return ParserResult::runtimeError( "Unrecognised verbosity, '" + verbosity + "'" );
+            return ParserResult::ok( ParseResultType::Matched );
+        };
+        auto const setReporter = [&]( std::string const& reporter ) {
+            IReporterRegistry::FactoryMap const& factories = getRegistryHub().getReporterRegistry().getFactories();
+
+            auto lcReporter = toLower( reporter );
+            auto result = factories.find( lcReporter );
+
+            if( factories.end() != result )
+                config.reporterName = lcReporter;
+            else
+                return ParserResult::runtimeError( "Unrecognized reporter, '" + reporter + "'. Check available with --list-reporters" );
+            return ParserResult::ok( ParseResultType::Matched );
+        };
+
+        auto cli
+            = ExeName( config.processName )
+            | Help( config.showHelp )
+            | Opt( config.listTests )
+                ["-l"]["--list-tests"]
+                ( "list all/matching test cases" )
+            | Opt( config.listTags )
+                ["-t"]["--list-tags"]
+                ( "list all/matching tags" )
+            | Opt( config.showSuccessfulTests )
+                ["-s"]["--success"]
+                ( "include successful tests in output" )
+            | Opt( config.shouldDebugBreak )
+                ["-b"]["--break"]
+                ( "break into debugger on failure" )
+            | Opt( config.noThrow )
+                ["-e"]["--nothrow"]
+                ( "skip exception tests" )
+            | Opt( config.showInvisibles )
+                ["-i"]["--invisibles"]
+                ( "show invisibles (tabs, newlines)" )
+            | Opt( config.outputFilename, "filename" )
+                ["-o"]["--out"]
+                ( "output filename" )
+            | Opt( setReporter, "name" )
+                ["-r"]["--reporter"]
+                ( "reporter to use (defaults to console)" )
+            | Opt( config.name, "name" )
+                ["-n"]["--name"]
+                ( "suite name" )
+            | Opt( [&]( bool ){ config.abortAfter = 1; } )
+                ["-a"]["--abort"]
+                ( "abort at first failure" )
+            | Opt( [&]( int x ){ config.abortAfter = x; }, "no. failures" )
+                ["-x"]["--abortx"]
+                ( "abort after x failures" )
+            | Opt( setWarning, "warning name" )
+                ["-w"]["--warn"]
+                ( "enable warnings" )
+            | 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" )
+            | Opt( config.filenamesAsTags )
+                ["-#"]["--filenames-as-tags"]
+                ( "adds a tag for the filename" )
+            | Opt( config.sectionsToRun, "section name" )
+                ["-c"]["--section"]
+                ( "specify section to run" )
+            | Opt( setVerbosity, "quiet|normal|high" )
+                ["-v"]["--verbosity"]
+                ( "set output verbosity" )
+            | Opt( config.listTestNamesOnly )
+                ["--list-test-names-only"]
+                ( "list all/matching test cases names only" )
+            | Opt( config.listReporters )
+                ["--list-reporters"]
+                ( "list all reporters" )
+            | Opt( setTestOrder, "decl|lex|rand" )
+                ["--order"]
+                ( "test case order (defaults to decl)" )
+            | Opt( setRngSeed, "'time'|number" )
+                ["--rng-seed"]
+                ( "set a specific seed for random numbers" )
+            | Opt( setColourUsage, "yes|no" )
+                ["--use-colour"]
+                ( "should output be colourised" )
+            | Opt( config.libIdentify )
+                ["--libidentify"]
+                ( "report name and version according to libidentify standard" )
+            | Opt( setWaitForKeypress, "never|start|exit|both" )
+                ["--wait-for-keypress"]
+                ( "waits for a keypress before exiting" )
+            | 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" );
+
+        return cli;
+    }
+
+} // end namespace Catch
+// end catch_commandline.cpp
+// start catch_common.cpp
+
+#include <cstring>
+#include <ostream>
+
+namespace Catch {
+
+    bool SourceLineInfo::operator == ( SourceLineInfo const& other ) const noexcept {
+        return line == other.line && (file == other.file || std::strcmp(file, other.file) == 0);
+    }
+    bool SourceLineInfo::operator < ( SourceLineInfo const& other ) const noexcept {
+        // We can assume that the same file will usually have the same pointer.
+        // Thus, if the pointers are the same, there is no point in calling the strcmp
+        return line < other.line || ( line == other.line && file != other.file && (std::strcmp(file, other.file) < 0));
+    }
+
+    std::ostream& operator << ( std::ostream& os, SourceLineInfo const& info ) {
+#ifndef __GNUG__
+        os << info.file << '(' << info.line << ')';
+#else
+        os << info.file << ':' << info.line;
+#endif
+        return os;
+    }
+
+    std::string StreamEndStop::operator+() const {
+        return std::string();
+    }
+
+    NonCopyable::NonCopyable() = default;
+    NonCopyable::~NonCopyable() = default;
+
+}
+// end catch_common.cpp
+// start catch_config.cpp
+
+namespace Catch {
+
+    Config::Config( ConfigData const& data )
+    :   m_data( data ),
+        m_stream( openStream() )
+    {
+        // 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);
+        }
+        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 : m_data.testsOrTags) {
+                parser.parse(testOrTags);
+            }
+        }
+        m_testSpec = parser.testSpec();
+    }
+
+    std::string const& Config::getFilename() const {
+        return m_data.outputFilename ;
+    }
+
+    bool Config::listTests() const          { return m_data.listTests; }
+    bool Config::listTestNamesOnly() const  { return m_data.listTestNamesOnly; }
+    bool Config::listTags() const           { return m_data.listTags; }
+    bool Config::listReporters() const      { return m_data.listReporters; }
+
+    std::string Config::getProcessName() const { return m_data.processName; }
+    std::string const& Config::getReporterName() const { return m_data.reporterName; }
+
+    std::vector<std::string> const& Config::getTestsOrTags() const { return m_data.testsOrTags; }
+    std::vector<std::string> const& Config::getSectionsToRun() const { return m_data.sectionsToRun; }
+
+    TestSpec const& Config::testSpec() const { return m_testSpec; }
+    bool Config::hasTestFilters() const { return m_hasTestFilters; }
+
+    bool Config::showHelp() const { return m_data.showHelp; }
+
+    // IConfig interface
+    bool Config::allowThrows() const                   { return !m_data.noThrow; }
+    std::ostream& Config::stream() const               { return m_stream->stream(); }
+    std::string Config::name() const                   { return m_data.name.empty() ? m_data.processName : m_data.name; }
+    bool Config::includeSuccessfulResults() const      { return m_data.showSuccessfulTests; }
+    bool Config::warnAboutMissingAssertions() const    { return !!(m_data.warnings & WarnAbout::NoAssertions); }
+    bool Config::warnAboutNoTests() const              { return !!(m_data.warnings & WarnAbout::NoTests); }
+    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; }
+    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);
+    }
+
+} // end namespace Catch
+// end catch_config.cpp
+// start catch_console_colour.cpp
+
+#if defined(__clang__)
+#    pragma clang diagnostic push
+#    pragma clang diagnostic ignored "-Wexit-time-destructors"
+#endif
+
+// start catch_errno_guard.h
+
+namespace Catch {
+
+    class ErrnoGuard {
+    public:
+        ErrnoGuard();
+        ~ErrnoGuard();
+    private:
+        int m_oldErrno;
+    };
+
+}
+
+// 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 {
+    namespace {
+
+        struct IColourImpl {
+            virtual ~IColourImpl() = default;
+            virtual void use( Colour::Code _colourCode ) = 0;
+        };
+
+        struct NoColourImpl : IColourImpl {
+            void use( Colour::Code ) override {}
+
+            static IColourImpl* instance() {
+                static NoColourImpl s_instance;
+                return &s_instance;
+            }
+        };
+
+    } // anon namespace
+} // namespace Catch
+
+#if !defined( CATCH_CONFIG_COLOUR_NONE ) && !defined( CATCH_CONFIG_COLOUR_WINDOWS ) && !defined( CATCH_CONFIG_COLOUR_ANSI )
+#   ifdef CATCH_PLATFORM_WINDOWS
+#       define CATCH_CONFIG_COLOUR_WINDOWS
+#   else
+#       define CATCH_CONFIG_COLOUR_ANSI
+#   endif
+#endif
+
+#if defined ( CATCH_CONFIG_COLOUR_WINDOWS ) /////////////////////////////////////////
+
+namespace Catch {
+namespace {
+
+    class Win32ColourImpl : public IColourImpl {
+    public:
+        Win32ColourImpl() : stdoutHandle( GetStdHandle(STD_OUTPUT_HANDLE) )
+        {
+            CONSOLE_SCREEN_BUFFER_INFO csbiInfo;
+            GetConsoleScreenBufferInfo( stdoutHandle, &csbiInfo );
+            originalForegroundAttributes = csbiInfo.wAttributes & ~( BACKGROUND_GREEN | BACKGROUND_RED | BACKGROUND_BLUE | BACKGROUND_INTENSITY );
+            originalBackgroundAttributes = csbiInfo.wAttributes & ~( FOREGROUND_GREEN | FOREGROUND_RED | FOREGROUND_BLUE | FOREGROUND_INTENSITY );
+        }
+
+        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 );
+                case Colour::Red:       return setTextAttribute( FOREGROUND_RED );
+                case Colour::Green:     return setTextAttribute( FOREGROUND_GREEN );
+                case Colour::Blue:      return setTextAttribute( FOREGROUND_BLUE );
+                case Colour::Cyan:      return setTextAttribute( FOREGROUND_BLUE | FOREGROUND_GREEN );
+                case Colour::Yellow:    return setTextAttribute( FOREGROUND_RED | FOREGROUND_GREEN );
+                case Colour::Grey:      return setTextAttribute( 0 );
+
+                case Colour::LightGrey:     return setTextAttribute( FOREGROUND_INTENSITY );
+                case Colour::BrightRed:     return setTextAttribute( FOREGROUND_INTENSITY | FOREGROUND_RED );
+                case Colour::BrightGreen:   return setTextAttribute( FOREGROUND_INTENSITY | FOREGROUND_GREEN );
+                case Colour::BrightWhite:   return setTextAttribute( FOREGROUND_INTENSITY | FOREGROUND_GREEN | FOREGROUND_RED | FOREGROUND_BLUE );
+                case Colour::BrightYellow:  return setTextAttribute( FOREGROUND_INTENSITY | FOREGROUND_RED | FOREGROUND_GREEN );
+
+                case Colour::Bright: CATCH_INTERNAL_ERROR( "not a colour" );
+
+                default:
+                    CATCH_ERROR( "Unknown colour requested" );
+            }
+        }
+
+    private:
+        void setTextAttribute( WORD _textAttribute ) {
+            SetConsoleTextAttribute( stdoutHandle, _textAttribute | originalBackgroundAttributes );
+        }
+        HANDLE stdoutHandle;
+        WORD originalForegroundAttributes;
+        WORD originalBackgroundAttributes;
+    };
+
+    IColourImpl* platformColourInstance() {
+        static Win32ColourImpl s_instance;
+
+        IConfigPtr config = getCurrentContext().getConfig();
+        UseColour::YesOrNo colourMode = config
+            ? config->useColour()
+            : UseColour::Auto;
+        if( colourMode == UseColour::Auto )
+            colourMode = UseColour::Yes;
+        return colourMode == UseColour::Yes
+            ? &s_instance
+            : NoColourImpl::instance();
+    }
+
+} // end anon namespace
+} // end namespace Catch
+
+#elif defined( CATCH_CONFIG_COLOUR_ANSI ) //////////////////////////////////////
+
+#include <unistd.h>
+
+namespace Catch {
+namespace {
+
+    // use POSIX/ ANSI console terminal codes
+    // Thanks to Adam Strzelecki for original contribution
+    // (http://github.com/nanoant)
+    // https://github.com/philsquared/Catch/pull/131
+    class PosixColourImpl : public IColourImpl {
+    public:
+        void use( Colour::Code _colourCode ) override {
+            switch( _colourCode ) {
+                case Colour::None:
+                case Colour::White:     return setColour( "[0m" );
+                case Colour::Red:       return setColour( "[0;31m" );
+                case Colour::Green:     return setColour( "[0;32m" );
+                case Colour::Blue:      return setColour( "[0;34m" );
+                case Colour::Cyan:      return setColour( "[0;36m" );
+                case Colour::Yellow:    return setColour( "[0;33m" );
+                case Colour::Grey:      return setColour( "[1;30m" );
+
+                case Colour::LightGrey:     return setColour( "[0;37m" );
+                case Colour::BrightRed:     return setColour( "[1;31m" );
+                case Colour::BrightGreen:   return setColour( "[1;32m" );
+                case Colour::BrightWhite:   return setColour( "[1;37m" );
+                case Colour::BrightYellow:  return setColour( "[1;33m" );
+
+                case Colour::Bright: CATCH_INTERNAL_ERROR( "not a colour" );
+                default: CATCH_INTERNAL_ERROR( "Unknown colour requested" );
+            }
+        }
+        static IColourImpl* instance() {
+            static PosixColourImpl s_instance;
+            return &s_instance;
+        }
+
+    private:
+        void setColour( const char* _escapeCode ) {
+            getCurrentContext().getConfig()->stream()
+                << '\033' << _escapeCode;
+        }
+    };
+
+    bool useColourOnPlatform() {
+        return
+#if defined(CATCH_PLATFORM_MAC) || defined(CATCH_PLATFORM_IPHONE)
+            !isDebuggerActive() &&
+#endif
+#if !(defined(__DJGPP__) && defined(__STRICT_ANSI__))
+            isatty(STDOUT_FILENO)
+#else
+            false
+#endif
+            ;
+    }
+    IColourImpl* platformColourInstance() {
+        ErrnoGuard guard;
+        IConfigPtr config = getCurrentContext().getConfig();
+        UseColour::YesOrNo colourMode = config
+            ? config->useColour()
+            : UseColour::Auto;
+        if( colourMode == UseColour::Auto )
+            colourMode = useColourOnPlatform()
+                ? UseColour::Yes
+                : UseColour::No;
+        return colourMode == UseColour::Yes
+            ? PosixColourImpl::instance()
+            : NoColourImpl::instance();
+    }
+
+} // end anon namespace
+} // end namespace Catch
+
+#else  // not Windows or ANSI ///////////////////////////////////////////////
+
+namespace Catch {
+
+    static IColourImpl* platformColourInstance() { return NoColourImpl::instance(); }
+
+} // end namespace Catch
+
+#endif // Windows/ ANSI/ None
+
+namespace Catch {
+
+    Colour::Colour( Code _colourCode ) { use( _colourCode ); }
+    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;
+    }
+
+    Colour::~Colour(){ if( !m_moved ) use( None ); }
+
+    void Colour::use( Code _colourCode ) {
+        static IColourImpl* impl = platformColourInstance();
+        // 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& ) {
+        return os;
+    }
+
+} // end namespace Catch
+
+#if defined(__clang__)
+#    pragma clang diagnostic pop
+#endif
+
+// end catch_console_colour.cpp
+// start catch_context.cpp
+
+namespace Catch {
+
+    class Context : public IMutableContext, NonCopyable {
+
+    public: // IContext
+        IResultCapture* getResultCapture() override {
+            return m_resultCapture;
+        }
+        IRunner* getRunner() override {
+            return m_runner;
+        }
+
+        IConfigPtr const& getConfig() const override {
+            return m_config;
+        }
+
+        ~Context() override;
+
+    public: // IMutableContext
+        void setResultCapture( IResultCapture* resultCapture ) override {
+            m_resultCapture = resultCapture;
+        }
+        void setRunner( IRunner* runner ) override {
+            m_runner = runner;
+        }
+        void setConfig( IConfigPtr const& config ) override {
+            m_config = config;
+        }
+
+        friend IMutableContext& getCurrentMutableContext();
+
+    private:
+        IConfigPtr m_config;
+        IRunner* m_runner = nullptr;
+        IResultCapture* m_resultCapture = nullptr;
+    };
+
+    IMutableContext *IMutableContext::currentContext = nullptr;
+
+    void IMutableContext::createContext()
+    {
+        currentContext = new Context();
+    }
+
+    void cleanUpContext() {
+        delete IMutableContext::currentContext;
+        IMutableContext::currentContext = nullptr;
+    }
+    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
+
+// start catch_debug_console.h
+
+#include <string>
+
+namespace Catch {
+    void writeToDebugConsole( std::string const& text );
+}
+
+// end catch_debug_console.h
+#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 ) {
+            ::OutputDebugStringA( text.c_str() );
+        }
+    }
+
+#else
+
+    namespace Catch {
+        void writeToDebugConsole( std::string const& text ) {
+            // !TBD: Need a version for Mac/ XCode and other IDEs
+            Catch::cout() << text;
+        }
+    }
+
+#endif // Platform
+// end catch_debug_console.cpp
+// start catch_debugger.cpp
+
+#if defined(CATCH_PLATFORM_MAC) || defined(CATCH_PLATFORM_IPHONE)
+
+#  include <cassert>
+#  include <sys/types.h>
+#  include <unistd.h>
+#  include <cstddef>
+#  include <ostream>
+
+#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:
+        // 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;
+
+            // Initialize the flags so that, if sysctl fails for some bizarre
+            // reason, we get a predictable result.
+
+            info.kp_proc.p_flag = 0;
+
+            // Initialize mib, which tells sysctl the info we want, in this case
+            // we're looking for information about a specific process ID.
+
+            mib[0] = CTL_KERN;
+            mib[1] = KERN_PROC;
+            mib[2] = KERN_PROC_PID;
+            mib[3] = getpid();
+
+            // Call sysctl.
+
+            size = sizeof(info);
+            if( sysctl(mib, sizeof(mib) / sizeof(*mib), &info, &size, nullptr, 0) != 0 ) {
+                Catch::cerr() << "\n** Call to sysctl failed - unable to determine if debugger is active **\n" << std::endl;
+                return false;
+            }
+
+            // We're being debugged if the P_TRACED flag is set.
+
+            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)
+    #include <fstream>
+    #include <string>
+
+    namespace Catch{
+        // The standard POSIX way of detecting a debugger is to attempt to
+        // ptrace() the process, but this needs to be done from a child and not
+        // this process itself to still allow attaching to this process later
+        // if wanted, so is rather heavy. Under Linux we have the PID of the
+        // "debugger" (which doesn't need to be gdb, of course, it could also
+        // be strace, for example) in /proc/$PID/status, so just get it from
+        // there instead.
+        bool isDebuggerActive(){
+            // Libstdc++ has a bug, where std::ifstream sets errno to 0
+            // This way our users can properly assert over errno values
+            ErrnoGuard guard;
+            std::ifstream in("/proc/self/status");
+            for( std::string line; std::getline(in, line); ) {
+                static const int PREFIX_LEN = 11;
+                if( line.compare(0, PREFIX_LEN, "TracerPid:\t") == 0 ) {
+                    // We're traced if the PID is not 0 and no other PID starts
+                    // with 0 digit, so it's enough to check for just a single
+                    // character.
+                    return line.length() > PREFIX_LEN && line[PREFIX_LEN] != '0';
+                }
+            }
+
+            return false;
+        }
+    } // namespace Catch
+#elif defined(_MSC_VER)
+    extern "C" __declspec(dllimport) int __stdcall IsDebuggerPresent();
+    namespace Catch {
+        bool isDebuggerActive() {
+            return IsDebuggerPresent() != 0;
+        }
+    }
+#elif defined(__MINGW32__)
+    extern "C" __declspec(dllimport) int __stdcall IsDebuggerPresent();
+    namespace Catch {
+        bool isDebuggerActive() {
+            return IsDebuggerPresent() != 0;
+        }
+    }
+#else
+    namespace Catch {
+       bool isDebuggerActive() { return false; }
+    }
+#endif // Platform
+// end catch_debugger.cpp
+// start catch_decomposer.cpp
+
+namespace Catch {
+
+    ITransientExpression::~ITransientExpression() = default;
+
+    void formatReconstructedExpression( std::ostream &os, std::string const& lhs, StringRef op, std::string const& rhs ) {
+        if( lhs.size() + rhs.size() < 40 &&
+                lhs.find('\n') == std::string::npos &&
+                rhs.find('\n') == std::string::npos )
+            os << lhs << " " << op << " " << rhs;
+        else
+            os << lhs << "\n" << op << "\n" << rhs;
+    }
+}
+// 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]]
+    void throw_exception(std::exception const& e) {
+        Catch::cerr() << "Catch will terminate because it needed to throw an exception.\n"
+                      << "The message was: " << e.what() << '\n';
+        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>
+
+namespace Catch {
+        ErrnoGuard::ErrnoGuard():m_oldErrno(errno){}
+        ErrnoGuard::~ErrnoGuard() { errno = m_oldErrno; }
+}
+// end catch_errno_guard.cpp
+// start catch_exception_translator_registry.cpp
+
+// start catch_exception_translator_registry.h
+
+#include <vector>
+#include <string>
+#include <memory>
+
+namespace Catch {
+
+    class ExceptionTranslatorRegistry : public IExceptionTranslatorRegistry {
+    public:
+        ~ExceptionTranslatorRegistry();
+        virtual void registerTranslator( const IExceptionTranslator* translator );
+        std::string translateActiveException() const override;
+        std::string tryTranslators() const;
+
+    private:
+        std::vector<std::unique_ptr<IExceptionTranslator const>> m_translators;
+    };
+}
+
+// end catch_exception_translator_registry.h
+#ifdef __OBJC__
+#import "Foundation/Foundation.h"
+#endif
+
+namespace Catch {
+
+    ExceptionTranslatorRegistry::~ExceptionTranslatorRegistry() {
+    }
+
+    void ExceptionTranslatorRegistry::registerTranslator( const IExceptionTranslator* translator ) {
+        m_translators.push_back( std::unique_ptr<const IExceptionTranslator>( translator ) );
+    }
+
+#if !defined(CATCH_CONFIG_DISABLE_EXCEPTIONS)
+    std::string ExceptionTranslatorRegistry::translateActiveException() const {
+        try {
+#ifdef __OBJC__
+            // In Objective-C try objective-c exceptions first
+            @try {
+                return tryTranslators();
+            }
+            @catch (NSException *exception) {
+                return Catch::Detail::stringify( [exception description] );
+            }
+#else
+            // Compiling a mixed mode project with MSVC means that CLR
+            // exceptions will be caught in (...) as well. However, these
+            // do not fill-in std::current_exception and thus lead to crash
+            // when attempting rethrow.
+            // /EHa switch also causes structured exceptions to be caught
+            // here, but they fill-in current_exception properly, so
+            // at worst the output should be a little weird, instead of
+            // causing a crash.
+            if (std::current_exception() == nullptr) {
+                return "Non C++ exception. Possibly a CLR exception.";
+            }
+            return tryTranslators();
+#endif
+        }
+        catch( TestFailureException& ) {
+            std::rethrow_exception(std::current_exception());
+        }
+        catch( std::exception& ex ) {
+            return ex.what();
+        }
+        catch( std::string& msg ) {
+            return msg;
+        }
+        catch( const char* msg ) {
+            return msg;
+        }
+        catch(...) {
+            return "Unknown exception";
+        }
+    }
+
+    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!");
+    }
+
+    std::string ExceptionTranslatorRegistry::tryTranslators() const {
+        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
+
+#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 {
+    //! Signals fatal error message to the run context
+    void reportFatal( char const * const message ) {
+        Catch::getCurrentContext().getResultCapture()->handleFatalErrorCondition( message );
+    }
+
+    //! 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[] = {
+        { 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" },
+    };
+
+    static LONG CALLBACK handleVectoredException(PEXCEPTION_POINTERS ExceptionInfo) {
+        for (auto const& def : signalDefs) {
+            if (ExceptionInfo->ExceptionRecord->ExceptionCode == def.id) {
+                reportFatal(def.name);
+            }
+        }
+        // If its not an exception we care about, pass it along.
+        // This stops us from eating debugger breaks etc.
+        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() {
+        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);
+        if (!exceptionHandlerHandle) {
+            CATCH_RUNTIME_ERROR("Could not register vectored exception handler");
+        }
+    }
+
+    void FatalConditionHandler::disengage_platform() {
+        if (!RemoveVectoredExceptionHandler(exceptionHandlerHandle)) {
+            CATCH_RUNTIME_ERROR("Could not unregister vectored exception handler");
+        }
+        exceptionHandlerHandle = nullptr;
+    }
+
+} // end namespace Catch
+
+#endif // CATCH_CONFIG_WINDOWS_SEH
+
+#if defined( CATCH_CONFIG_POSIX_SIGNALS )
+
+#include <signal.h>
+
+namespace Catch {
+
+    struct SignalDefs {
+        int id;
+        const char* name;
+    };
+
+    static SignalDefs signalDefs[] = {
+        { SIGINT,  "SIGINT - Terminal interrupt signal" },
+        { SIGILL,  "SIGILL - Illegal instruction signal" },
+        { SIGFPE,  "SIGFPE - Floating point error signal" },
+        { SIGSEGV, "SIGSEGV - Segmentation violation signal" },
+        { SIGTERM, "SIGTERM - Termination request signal" },
+        { SIGABRT, "SIGABRT - Abort (abnormal termination) signal" }
+    };
+
+// Older GCCs trigger -Wmissing-field-initializers for T foo = {}
+// which is zero initialization, but not explicit. We want to avoid
+// that.
+#if defined(__GNUC__)
+#    pragma GCC diagnostic push
+#    pragma GCC diagnostic ignored "-Wmissing-field-initializers"
+#endif
+
+    static char* altStackMem = nullptr;
+    static std::size_t altStackSize = 0;
+    static stack_t oldSigStack{};
+    static struct sigaction oldSigActions[sizeof(signalDefs) / sizeof(SignalDefs)]{};
+
+    static void restorePreviousSignalHandlers() {
+        // We set signal handlers back to the previous ones. Hopefully
+        // nobody overwrote them in the meantime, and doesn't expect
+        // their signal handlers to live past ours given that they
+        // installed them after ours..
+        for (std::size_t i = 0; i < sizeof(signalDefs) / sizeof(SignalDefs); ++i) {
+            sigaction(signalDefs[i].id, &oldSigActions[i], nullptr);
+        }
+        // Return the old stack
+        sigaltstack(&oldSigStack, nullptr);
+    }
+
+    static void handleSignal( int sig ) {
+        char const * name = "<unknown signal>";
+        for (auto const& def : signalDefs) {
+            if (sig == def.id) {
+                name = def.name;
+                break;
+            }
+        }
+        // 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() {
+        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 = altStackSize;
+        sigStack.ss_flags = 0;
+        sigaltstack(&sigStack, &oldSigStack);
+        struct sigaction sa = { };
+
+        sa.sa_handler = handleSignal;
+        sa.sa_flags = SA_ONSTACK;
+        for (std::size_t i = 0; i < sizeof(signalDefs)/sizeof(SignalDefs); ++i) {
+            sigaction(signalDefs[i].id, &sa, &oldSigActions[i]);
+        }
+    }
+
+#if defined(__GNUC__)
+#    pragma GCC diagnostic pop
+#endif
+
+    void FatalConditionHandler::disengage_platform() {
+        restorePreviousSignalHandlers();
+    }
+
+} // end namespace Catch
+
+#endif // CATCH_CONFIG_POSIX_SIGNALS
+// end catch_fatal_condition.cpp
+// start catch_generators.cpp
+
+#include <limits>
+#include <set>
+
+namespace Catch {
+
+IGeneratorTracker::~IGeneratorTracker() {}
+
+const char* GeneratorException::what() const noexcept {
+    return m_msg;
+}
+
+namespace Generators {
+
+    GeneratorUntypedBase::~GeneratorUntypedBase() {}
+
+    auto acquireGeneratorTracker( StringRef generatorName, SourceLineInfo const& lineInfo ) -> IGeneratorTracker& {
+        return getResultCapture().acquireGeneratorTracker( generatorName, lineInfo );
+    }
+
+} // namespace Generators
+} // namespace Catch
+// end catch_generators.cpp
+// start catch_interfaces_capture.cpp
+
+namespace Catch {
+    IResultCapture::~IResultCapture() = default;
+}
+// end catch_interfaces_capture.cpp
+// start catch_interfaces_config.cpp
+
+namespace Catch {
+    IConfig::~IConfig() = default;
+}
+// end catch_interfaces_config.cpp
+// start catch_interfaces_exception.cpp
+
+namespace Catch {
+    IExceptionTranslator::~IExceptionTranslator() = default;
+    IExceptionTranslatorRegistry::~IExceptionTranslatorRegistry() = default;
+}
+// end catch_interfaces_exception.cpp
+// start catch_interfaces_registry_hub.cpp
+
+namespace Catch {
+    IRegistryHub::~IRegistryHub() = default;
+    IMutableRegistryHub::~IMutableRegistryHub() = default;
+}
+// end catch_interfaces_registry_hub.cpp
+// start catch_interfaces_reporter.cpp
+
+// start catch_reporter_listening.h
+
+namespace Catch {
+
+    class ListeningReporter : public IStreamingReporter {
+        using Reporters = std::vector<IStreamingReporterPtr>;
+        Reporters m_listeners;
+        IStreamingReporterPtr m_reporter = nullptr;
+        ReporterPreferences m_preferences;
+
+    public:
+        ListeningReporter();
+
+        void addListener( IStreamingReporterPtr&& listener );
+        void addReporter( IStreamingReporterPtr&& reporter );
+
+    public: // IStreamingReporter
+
+        ReporterPreferences getPreferences() const override;
+
+        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 benchmarkFailed(std::string const&) override;
+#endif // CATCH_CONFIG_ENABLE_BENCHMARKING
+
+        void testRunStarting( TestRunInfo const& testRunInfo ) override;
+        void testGroupStarting( GroupInfo const& groupInfo ) override;
+        void testCaseStarting( TestCaseInfo const& testInfo ) override;
+        void sectionStarting( SectionInfo const& sectionInfo ) override;
+        void assertionStarting( AssertionInfo const& assertionInfo ) override;
+
+        // The return value indicates if the messages buffer should be cleared:
+        bool assertionEnded( AssertionStats const& assertionStats ) override;
+        void sectionEnded( SectionStats const& sectionStats ) override;
+        void testCaseEnded( TestCaseStats const& testCaseStats ) override;
+        void testGroupEnded( TestGroupStats const& testGroupStats ) override;
+        void testRunEnded( TestRunStats const& testRunStats ) override;
+
+        void skipTest( TestCaseInfo const& testInfo ) override;
+        bool isMulti() const override;
+
+    };
+
+} // end namespace Catch
+
+// end catch_reporter_listening.h
+namespace Catch {
+
+    ReporterConfig::ReporterConfig( IConfigPtr const& _fullConfig )
+    :   m_stream( &_fullConfig->stream() ), m_fullConfig( _fullConfig ) {}
+
+    ReporterConfig::ReporterConfig( IConfigPtr const& _fullConfig, std::ostream& _stream )
+    :   m_stream( &_stream ), m_fullConfig( _fullConfig ) {}
+
+    std::ostream& ReporterConfig::stream() const { return *m_stream; }
+    IConfigPtr ReporterConfig::fullConfig() const { return m_fullConfig; }
+
+    TestRunInfo::TestRunInfo( std::string const& _name ) : name( _name ) {}
+
+    GroupInfo::GroupInfo(  std::string const& _name,
+                           std::size_t _groupIndex,
+                           std::size_t _groupsCount )
+    :   name( _name ),
+        groupIndex( _groupIndex ),
+        groupsCounts( _groupsCount )
+    {}
+
+     AssertionStats::AssertionStats( AssertionResult const& _assertionResult,
+                                     std::vector<MessageInfo> const& _infoMessages,
+                                     Totals const& _totals )
+    :   assertionResult( _assertionResult ),
+        infoMessages( _infoMessages ),
+        totals( _totals )
+    {
+        assertionResult.m_resultData.lazyExpression.m_transientExpression = _assertionResult.m_resultData.lazyExpression.m_transientExpression;
+
+        if( assertionResult.hasMessage() ) {
+            // Copy message into messages list.
+            // !TBD This should have been done earlier, somewhere
+            MessageBuilder builder( assertionResult.getTestMacroName(), assertionResult.getSourceInfo(), assertionResult.getResultType() );
+            builder << assertionResult.getMessage();
+            builder.m_info.message = builder.m_stream.str();
+
+            infoMessages.push_back( builder.m_info );
+        }
+    }
+
+     AssertionStats::~AssertionStats() = default;
+
+    SectionStats::SectionStats(  SectionInfo const& _sectionInfo,
+                                 Counts const& _assertions,
+                                 double _durationInSeconds,
+                                 bool _missingAssertions )
+    :   sectionInfo( _sectionInfo ),
+        assertions( _assertions ),
+        durationInSeconds( _durationInSeconds ),
+        missingAssertions( _missingAssertions )
+    {}
+
+    SectionStats::~SectionStats() = default;
+
+    TestCaseStats::TestCaseStats(  TestCaseInfo const& _testInfo,
+                                   Totals const& _totals,
+                                   std::string const& _stdOut,
+                                   std::string const& _stdErr,
+                                   bool _aborting )
+    : testInfo( _testInfo ),
+        totals( _totals ),
+        stdOut( _stdOut ),
+        stdErr( _stdErr ),
+        aborting( _aborting )
+    {}
+
+    TestCaseStats::~TestCaseStats() = default;
+
+    TestGroupStats::TestGroupStats( GroupInfo const& _groupInfo,
+                                    Totals const& _totals,
+                                    bool _aborting )
+    :   groupInfo( _groupInfo ),
+        totals( _totals ),
+        aborting( _aborting )
+    {}
+
+    TestGroupStats::TestGroupStats( GroupInfo const& _groupInfo )
+    :   groupInfo( _groupInfo ),
+        aborting( false )
+    {}
+
+    TestGroupStats::~TestGroupStats() = default;
+
+    TestRunStats::TestRunStats(   TestRunInfo const& _runInfo,
+                    Totals const& _totals,
+                    bool _aborting )
+    :   runInfo( _runInfo ),
+        totals( _totals ),
+        aborting( _aborting )
+    {}
+
+    TestRunStats::~TestRunStats() = default;
+
+    void IStreamingReporter::fatalErrorEncountered( StringRef ) {}
+    bool IStreamingReporter::isMulti() const { return false; }
+
+    IReporterFactory::~IReporterFactory() = default;
+    IReporterRegistry::~IReporterRegistry() = default;
+
+} // end namespace Catch
+// end catch_interfaces_reporter.cpp
+// start catch_interfaces_runner.cpp
+
+namespace Catch {
+    IRunner::~IRunner() = default;
+}
+// end catch_interfaces_runner.cpp
+// start catch_interfaces_testcase.cpp
+
+namespace Catch {
+    ITestInvoker::~ITestInvoker() = default;
+    ITestCaseRegistry::~ITestCaseRegistry() = default;
+}
+// end catch_interfaces_testcase.cpp
+// start catch_leak_detector.cpp
+
+#ifdef CATCH_CONFIG_WINDOWS_CRTDBG
+#include <crtdbg.h>
+
+namespace Catch {
+
+    LeakDetector::LeakDetector() {
+        int flag = _CrtSetDbgFlag(_CRTDBG_REPORT_FLAG);
+        flag |= _CRTDBG_LEAK_CHECK_DF;
+        flag |= _CRTDBG_ALLOC_MEM_DF;
+        _CrtSetDbgFlag(flag);
+        _CrtSetReportMode(_CRT_WARN, _CRTDBG_MODE_FILE | _CRTDBG_MODE_DEBUG);
+        _CrtSetReportFile(_CRT_WARN, _CRTDBG_FILE_STDERR);
+        // Change this to leaking allocation's number to break there
+        _CrtSetBreakAlloc(-1);
+    }
+}
+
+#else
+
+    Catch::LeakDetector::LeakDetector() {}
+
+#endif
+
+Catch::LeakDetector::~LeakDetector() {
+    Catch::cleanUp();
+}
+// end catch_leak_detector.cpp
+// start catch_list.cpp
+
+// start catch_list.h
+
+#include <set>
+
+namespace Catch {
+
+    std::size_t listTests( Config const& config );
+
+    std::size_t listTestsNamesOnly( Config const& config );
+
+    struct TagInfo {
+        void add( std::string const& spelling );
+        std::string all() const;
+
+        std::set<std::string> spellings;
+        std::size_t count = 0;
+    };
+
+    std::size_t listTags( Config const& config );
+
+    std::size_t listReporters();
+
+    Option<std::size_t> list( std::shared_ptr<Config> const& config );
+
+} // end namespace Catch
+
+// end catch_list.h
+// start catch_text.h
+
+namespace Catch {
+    using namespace clara::TextFlow;
+}
+
+// end catch_text.h
+#include <limits>
+#include <algorithm>
+#include <iomanip>
+
+namespace Catch {
+
+    std::size_t listTests( Config const& config ) {
+        TestSpec const& testSpec = config.testSpec();
+        if( config.hasTestFilters() )
+            Catch::cout() << "Matching test cases:\n";
+        else {
+            Catch::cout() << "All available test cases:\n";
+        }
+
+        auto matchedTestCases = filterTests( getAllTestCasesSorted( config ), testSpec, config );
+        for( auto const& testCaseInfo : matchedTestCases ) {
+            Colour::Code colour = testCaseInfo.isHidden()
+                ? Colour::SecondaryText
+                : Colour::None;
+            Colour colourGuard( colour );
+
+            Catch::cout() << Column( testCaseInfo.name ).initialIndent( 2 ).indent( 4 ) << "\n";
+            if( config.verbosity() >= Verbosity::High ) {
+                Catch::cout() << Column( Catch::Detail::stringify( testCaseInfo.lineInfo ) ).indent(4) << std::endl;
+                std::string description = testCaseInfo.description;
+                if( description.empty() )
+                    description = "(NO DESCRIPTION)";
+                Catch::cout() << Column( description ).indent(4) << std::endl;
+            }
+            if( !testCaseInfo.tags.empty() )
+                Catch::cout() << Column( testCaseInfo.tagsAsString() ).indent( 6 ) << "\n";
+        }
+
+        if( !config.hasTestFilters() )
+            Catch::cout() << pluralise( matchedTestCases.size(), "test case" ) << '\n' << std::endl;
+        else
+            Catch::cout() << pluralise( matchedTestCases.size(), "matching test case" ) << '\n' << std::endl;
+        return matchedTestCases.size();
+    }
+
+    std::size_t listTestsNamesOnly( Config const& config ) {
+        TestSpec const& testSpec = config.testSpec();
+        std::size_t matchedTests = 0;
+        std::vector<TestCase> matchedTestCases = filterTests( getAllTestCasesSorted( config ), testSpec, config );
+        for( auto const& testCaseInfo : matchedTestCases ) {
+            matchedTests++;
+            if( startsWith( testCaseInfo.name, '#' ) )
+               Catch::cout() << '"' << testCaseInfo.name << '"';
+            else
+               Catch::cout() << testCaseInfo.name;
+            if ( config.verbosity() >= Verbosity::High )
+                Catch::cout() << "\t@" << testCaseInfo.lineInfo;
+            Catch::cout() << std::endl;
+        }
+        return matchedTests;
+    }
+
+    void TagInfo::add( std::string const& spelling ) {
+        ++count;
+        spellings.insert( spelling );
+    }
+
+    std::string TagInfo::all() const {
+        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 const& testSpec = config.testSpec();
+        if( config.hasTestFilters() )
+            Catch::cout() << "Tags for matching test cases:\n";
+        else {
+            Catch::cout() << "All available tags:\n";
+        }
+
+        std::map<std::string, TagInfo> tagCounts;
+
+        std::vector<TestCase> matchedTestCases = filterTests( getAllTestCasesSorted( config ), testSpec, config );
+        for( auto const& testCase : matchedTestCases ) {
+            for( auto const& tagName : testCase.getTestCaseInfo().tags ) {
+                std::string lcaseTagName = toLower( tagName );
+                auto countIt = tagCounts.find( lcaseTagName );
+                if( countIt == tagCounts.end() )
+                    countIt = tagCounts.insert( std::make_pair( lcaseTagName, TagInfo() ) ).first;
+                countIt->second.add( tagName );
+            }
+        }
+
+        for( auto const& tagCount : tagCounts ) {
+            ReusableStringStream rss;
+            rss << "  " << std::setw(2) << tagCount.second.count << "  ";
+            auto str = rss.str();
+            auto wrapper = Column( tagCount.second.all() )
+                                                    .initialIndent( 0 )
+                                                    .indent( str.size() )
+                                                    .width( CATCH_CONFIG_CONSOLE_WIDTH-10 );
+            Catch::cout() << str << wrapper << '\n';
+        }
+        Catch::cout() << pluralise( tagCounts.size(), "tag" ) << '\n' << std::endl;
+        return tagCounts.size();
+    }
+
+    std::size_t listReporters() {
+        Catch::cout() << "Available reporters:\n";
+        IReporterRegistry::FactoryMap const& factories = getRegistryHub().getReporterRegistry().getFactories();
+        std::size_t maxNameLen = 0;
+        for( auto const& factoryKvp : factories )
+            maxNameLen = (std::max)( maxNameLen, factoryKvp.first.size() );
+
+        for( auto const& factoryKvp : factories ) {
+            Catch::cout()
+                    << Column( factoryKvp.first + ":" )
+                            .indent(2)
+                            .width( 5+maxNameLen )
+                    +  Column( factoryKvp.second->getDescription() )
+                            .initialIndent(0)
+                            .indent(2)
+                            .width( CATCH_CONFIG_CONSOLE_WIDTH - maxNameLen-8 )
+                    << "\n";
+        }
+        Catch::cout() << std::endl;
+        return factories.size();
+    }
+
+    Option<std::size_t> list( std::shared_ptr<Config> const& config ) {
+        Option<std::size_t> listedCount;
+        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;
+    }
+
+} // end namespace Catch
+// end catch_list.cpp
+// start catch_matchers.cpp
+
+namespace Catch {
+namespace Matchers {
+    namespace Impl {
+
+        std::string MatcherUntypedBase::toString() const {
+            if( m_cachedToString.empty() )
+                m_cachedToString = describe();
+            return m_cachedToString;
+        }
+
+        MatcherUntypedBase::~MatcherUntypedBase() = default;
+
+    } // namespace Impl
+} // namespace Matchers
+
+using namespace Matchers;
+using Matchers::Impl::MatcherBase;
+
+} // 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
+
+namespace Catch {
+    bool isnan(float f);
+    bool isnan(double d);
+}
+
+// end catch_polyfills.hpp
+// start catch_to_string.hpp
+
+#include <string>
+
+namespace Catch {
+    template <typename T>
+    std::string to_string(T const& t) {
+#if defined(CATCH_CONFIG_CPP11_TO_STRING)
+        return std::to_string(t);
+#else
+        ReusableStringStream rss;
+        rss << t;
+        return rss.str();
+#endif
+    }
+} // 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 {
+
+    int32_t convert(float f) {
+        static_assert(sizeof(float) == sizeof(int32_t), "Important ULP matcher assumption violated");
+        int32_t i;
+        std::memcpy(&i, &f, sizeof(f));
+        return i;
+    }
+
+    int64_t convert(double d) {
+        static_assert(sizeof(double) == sizeof(int64_t), "Important ULP matcher assumption violated");
+        int64_t i;
+        std::memcpy(&i, &d, sizeof(d));
+        return i;
+    }
+
+    template <typename FP>
+    bool almostEqualUlps(FP lhs, FP rhs, uint64_t maxUlpDiff) {
+        // Comparison with NaN should always be false.
+        // 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;
+    }
+
+#if defined(CATCH_CONFIG_GLOBAL_NEXTAFTER)
+
+    float nextafter(float x, float y) {
+        return ::nextafterf(x, y);
+    }
+
+    double nextafter(double x, double y) {
+        return ::nextafter(x, y);
+    }
+
+#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;
+}
+
+} // 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 << '.'
+            << " Margin has to be non-negative.");
+    }
+
+    // Performs equivalent check of std::fabs(lhs - rhs) <= margin
+    // But without the subtraction to allow for INFINITY in comparison
+    bool WithinAbsMatcher::match(double const& matchee) const {
+        return (matchee + m_margin >= m_target) && (m_target + m_margin >= matchee);
+    }
+
+    std::string WithinAbsMatcher::describe() const {
+        return "is within " + ::Catch::Detail::stringify(m_margin) + " of " + ::Catch::Detail::stringify(m_target);
+    }
+
+    WithinUlpsMatcher::WithinUlpsMatcher(double target, uint64_t ulps, FloatingPointKind baseType)
+        :m_target{ target }, m_ulps{ ulps }, m_type{ baseType } {
+        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__)
+#pragma clang diagnostic push
+// Clang <3.5 reports on the default branch in the switch below
+#pragma clang diagnostic ignored "-Wunreachable-code"
+#endif
+
+    bool WithinUlpsMatcher::match(double const& matchee) const {
+        switch (m_type) {
+        case FloatingPointKind::Float:
+            return almostEqualUlps<float>(static_cast<float>(matchee), static_cast<float>(m_target), m_ulps);
+        case FloatingPointKind::Double:
+            return almostEqualUlps<double>(matchee, m_target, m_ulps);
+        default:
+            CATCH_INTERNAL_ERROR( "Unknown FloatingPointKind value" );
+        }
+    }
+
+#if defined(__clang__)
+#pragma clang diagnostic pop
+#endif
+
+    std::string WithinUlpsMatcher::describe() const {
+        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, uint64_t maxUlpDiff) {
+    return Floating::WithinUlpsMatcher(target, maxUlpDiff, Floating::FloatingPointKind::Double);
+}
+
+Floating::WithinUlpsMatcher WithinULP(float target, uint64_t maxUlpDiff) {
+    return Floating::WithinUlpsMatcher(target, maxUlpDiff, Floating::FloatingPointKind::Float);
+}
+
+Floating::WithinAbsMatcher WithinAbs(double target, double margin) {
+    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
+
+std::string Catch::Matchers::Generic::Detail::finalizeDescription(const std::string& desc) {
+    if (desc.empty()) {
+        return "matches undescribed predicate";
+    } else {
+        return "matches predicate: \"" + desc + '"';
+    }
+}
+// end catch_matchers_generic.cpp
+// start catch_matchers_string.cpp
+
+#include <regex>
+
+namespace Catch {
+namespace Matchers {
+
+    namespace StdString {
+
+        CasedString::CasedString( std::string const& str, CaseSensitive::Choice caseSensitivity )
+        :   m_caseSensitivity( caseSensitivity ),
+            m_str( adjustString( str ) )
+        {}
+        std::string CasedString::adjustString( std::string const& str ) const {
+            return m_caseSensitivity == CaseSensitive::No
+                   ? toLower( str )
+                   : str;
+        }
+        std::string CasedString::caseSensitivitySuffix() const {
+            return m_caseSensitivity == CaseSensitive::No
+                   ? " (case insensitive)"
+                   : std::string();
+        }
+
+        StringMatcherBase::StringMatcherBase( std::string const& operation, CasedString const& comparator )
+        : m_comparator( comparator ),
+          m_operation( operation ) {
+        }
+
+        std::string StringMatcherBase::describe() const {
+            std::string description;
+            description.reserve(5 + m_operation.size() + m_comparator.m_str.size() +
+                                        m_comparator.caseSensitivitySuffix().size());
+            description += m_operation;
+            description += ": \"";
+            description += m_comparator.m_str;
+            description += "\"";
+            description += m_comparator.caseSensitivitySuffix();
+            return description;
+        }
+
+        EqualsMatcher::EqualsMatcher( CasedString const& comparator ) : StringMatcherBase( "equals", comparator ) {}
+
+        bool EqualsMatcher::match( std::string const& source ) const {
+            return m_comparator.adjustString( source ) == m_comparator.m_str;
+        }
+
+        ContainsMatcher::ContainsMatcher( CasedString const& comparator ) : StringMatcherBase( "contains", comparator ) {}
+
+        bool ContainsMatcher::match( std::string const& source ) const {
+            return contains( m_comparator.adjustString( source ), m_comparator.m_str );
+        }
+
+        StartsWithMatcher::StartsWithMatcher( CasedString const& comparator ) : StringMatcherBase( "starts with", comparator ) {}
+
+        bool StartsWithMatcher::match( std::string const& source ) const {
+            return startsWith( m_comparator.adjustString( source ), m_comparator.m_str );
+        }
+
+        EndsWithMatcher::EndsWithMatcher( CasedString const& comparator ) : StringMatcherBase( "ends with", comparator ) {}
+
+        bool EndsWithMatcher::match( std::string const& source ) const {
+            return endsWith( m_comparator.adjustString( source ), m_comparator.m_str );
+        }
+
+        RegexMatcher::RegexMatcher(std::string regex, CaseSensitive::Choice caseSensitivity): m_regex(std::move(regex)), m_caseSensitivity(caseSensitivity) {}
+
+        bool RegexMatcher::match(std::string const& matchee) const {
+            auto flags = std::regex::ECMAScript; // ECMAScript is the default syntax option anyway
+            if (m_caseSensitivity == CaseSensitive::Choice::No) {
+                flags |= std::regex::icase;
+            }
+            auto reg = std::regex(m_regex, flags);
+            return std::regex_match(matchee, reg);
+        }
+
+        std::string RegexMatcher::describe() const {
+            return "matches " + ::Catch::Detail::stringify(m_regex) + ((m_caseSensitivity == CaseSensitive::Choice::Yes)? " case sensitively" : " case insensitively");
+        }
+
+    } // namespace StdString
+
+    StdString::EqualsMatcher Equals( std::string const& str, CaseSensitive::Choice caseSensitivity ) {
+        return StdString::EqualsMatcher( StdString::CasedString( str, caseSensitivity) );
+    }
+    StdString::ContainsMatcher Contains( std::string const& str, CaseSensitive::Choice caseSensitivity ) {
+        return StdString::ContainsMatcher( StdString::CasedString( str, caseSensitivity) );
+    }
+    StdString::EndsWithMatcher EndsWith( std::string const& str, CaseSensitive::Choice caseSensitivity ) {
+        return StdString::EndsWithMatcher( StdString::CasedString( str, caseSensitivity) );
+    }
+    StdString::StartsWithMatcher StartsWith( std::string const& str, CaseSensitive::Choice caseSensitivity ) {
+        return StdString::StartsWithMatcher( StdString::CasedString( str, caseSensitivity) );
+    }
+
+    StdString::RegexMatcher Matches(std::string const& regex, CaseSensitive::Choice caseSensitivity) {
+        return StdString::RegexMatcher(regex, caseSensitivity);
+    }
+
+} // namespace Matchers
+} // namespace Catch
+// end catch_matchers_string.cpp
+// start catch_message.cpp
+
+// start catch_uncaught_exceptions.h
+
+namespace Catch {
+    bool uncaught_exceptions();
+} // end namespace Catch
+
+// end catch_uncaught_exceptions.h
+#include <cassert>
+#include <stack>
+
+namespace Catch {
+
+    MessageInfo::MessageInfo(   StringRef const& _macroName,
+                                SourceLineInfo const& _lineInfo,
+                                ResultWas::OfType _type )
+    :   macroName( _macroName ),
+        lineInfo( _lineInfo ),
+        type( _type ),
+        sequence( ++globalCount )
+    {}
+
+    bool MessageInfo::operator==( MessageInfo const& other ) const {
+        return sequence == other.sequence;
+    }
+
+    bool MessageInfo::operator<( MessageInfo const& other ) const {
+        return sequence < other.sequence;
+    }
+
+    // This may need protecting if threading support is added
+    unsigned int MessageInfo::globalCount = 0;
+
+    ////////////////////////////////////////////////////////////////////////////
+
+    Catch::MessageBuilder::MessageBuilder( StringRef const& macroName,
+                                           SourceLineInfo const& lineInfo,
+                                           ResultWas::OfType type )
+        :m_info(macroName, lineInfo, type) {}
+
+    ////////////////////////////////////////////////////////////////////////////
+
+    ScopedMessage::ScopedMessage( MessageBuilder const& builder )
+    : m_info( builder.m_info ), 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() && !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(static_cast<unsigned char>(names[start]))) {
+                ++start;
+            }
+            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;
+        for (size_t pos = 0; pos < names.size(); ++pos) {
+            char c = names[pos];
+            switch (c) {
+            case '[':
+            case '{':
+            case '(':
+            // It is basically impossible to disambiguate between
+            // comparison and start of template args in this context
+//            case '<':
+                openings.push(c);
+                break;
+            case ']':
+            case '}':
+            case ')':
+//           case '>':
+                openings.pop();
+                break;
+            case '"':
+            case '\'':
+                pos = skipq(pos, c);
+                break;
+            case ',':
+                if (start != pos && openings.empty()) {
+                    m_messages.emplace_back(macroName, lineInfo, resultType);
+                    m_messages.back().message = static_cast<std::string>(trimmed(start, pos));
+                    m_messages.back().message += " := ";
+                    start = pos;
+                }
+            }
+        }
+        assert(openings.empty() && "Mismatched openings");
+        m_messages.emplace_back(macroName, lineInfo, resultType);
+        m_messages.back().message = static_cast<std::string>(trimmed(start, names.size() - 1));
+        m_messages.back().message += " := ";
+    }
+    Capturer::~Capturer() {
+        if ( !uncaught_exceptions() ){
+            assert( m_captured == m_messages.size() );
+            for( size_t i = 0; i < m_captured; ++i  )
+                m_resultCapture.popScopedMessage( m_messages[i] );
+        }
+    }
+
+    void Capturer::captureValue( size_t index, std::string const& value ) {
+        assert( index < m_messages.size() );
+        m_messages[index].message += value;
+        m_resultCapture.pushScopedMessage( m_messages[index] );
+        m_captured++;
+    }
+
+} // end namespace Catch
+// end catch_message.cpp
+// start catch_output_redirect.cpp
+
+// start catch_output_redirect.h
+#ifndef TWOBLUECUBES_CATCH_OUTPUT_REDIRECT_H
+#define TWOBLUECUBES_CATCH_OUTPUT_REDIRECT_H
+
+#include <cstdio>
+#include <iosfwd>
+#include <string>
+
+namespace Catch {
+
+    class RedirectedStream {
+        std::ostream& m_originalStream;
+        std::ostream& m_redirectionStream;
+        std::streambuf* m_prevBuf;
+
+    public:
+        RedirectedStream( std::ostream& originalStream, std::ostream& redirectionStream );
+        ~RedirectedStream();
+    };
+
+    class RedirectedStdOut {
+        ReusableStringStream m_rss;
+        RedirectedStream m_cout;
+    public:
+        RedirectedStdOut();
+        auto str() const -> std::string;
+    };
+
+    // StdErr has two constituent streams in C++, std::cerr and std::clog
+    // This means that we need to redirect 2 streams into 1 to keep proper
+    // order of writes
+    class RedirectedStdErr {
+        ReusableStringStream m_rss;
+        RedirectedStream m_cerr;
+        RedirectedStream m_clog;
+    public:
+        RedirectedStdErr();
+        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
+    // to create a file inside system folder, thus requiring elevated
+    // privileges for the binary), so we have to use tmpnam(_s) and
+    // create the file ourselves there.
+    class TempFile {
+    public:
+        TempFile(TempFile const&) = delete;
+        TempFile& operator=(TempFile const&) = delete;
+        TempFile(TempFile&&) = delete;
+        TempFile& operator=(TempFile&&) = delete;
+
+        TempFile();
+        ~TempFile();
+
+        std::FILE* getFile();
+        std::string getContents();
+
+    private:
+        std::FILE* m_file = nullptr;
+    #if defined(_MSC_VER)
+        char m_buffer[L_tmpnam] = { 0 };
+    #endif
+    };
+
+    class OutputRedirect {
+    public:
+        OutputRedirect(OutputRedirect const&) = delete;
+        OutputRedirect& operator=(OutputRedirect const&) = delete;
+        OutputRedirect(OutputRedirect&&) = delete;
+        OutputRedirect& operator=(OutputRedirect&&) = delete;
+
+        OutputRedirect(std::string& stdout_dest, std::string& stderr_dest);
+        ~OutputRedirect();
+
+    private:
+        int m_originalStdout = -1;
+        int m_originalStderr = -1;
+        TempFile m_stdoutFile;
+        TempFile m_stderrFile;
+        std::string& m_stdoutDest;
+        std::string& m_stderrDest;
+    };
+
+#endif
+
+} // end namespace Catch
+
+#endif // TWOBLUECUBES_CATCH_OUTPUT_REDIRECT_H
+// end catch_output_redirect.h
+#include <cstdio>
+#include <cstring>
+#include <fstream>
+#include <sstream>
+#include <stdexcept>
+
+#if defined(CATCH_CONFIG_NEW_CAPTURE)
+    #if defined(_MSC_VER)
+    #include <io.h>      //_dup and _dup2
+    #define dup _dup
+    #define dup2 _dup2
+    #define fileno _fileno
+    #else
+    #include <unistd.h>  // dup and dup2
+    #endif
+#endif
+
+namespace Catch {
+
+    RedirectedStream::RedirectedStream( std::ostream& originalStream, std::ostream& redirectionStream )
+    :   m_originalStream( originalStream ),
+        m_redirectionStream( redirectionStream ),
+        m_prevBuf( m_originalStream.rdbuf() )
+    {
+        m_originalStream.rdbuf( m_redirectionStream.rdbuf() );
+    }
+
+    RedirectedStream::~RedirectedStream() {
+        m_originalStream.rdbuf( m_prevBuf );
+    }
+
+    RedirectedStdOut::RedirectedStdOut() : m_cout( Catch::cout(), m_rss.get() ) {}
+    auto RedirectedStdOut::str() const -> std::string { return m_rss.str(); }
+
+    RedirectedStdErr::RedirectedStdErr()
+    :   m_cerr( Catch::cerr(), m_rss.get() ),
+        m_clog( Catch::clog(), m_rss.get() )
+    {}
+    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)
+    TempFile::TempFile() {
+        if (tmpnam_s(m_buffer)) {
+            CATCH_RUNTIME_ERROR("Could not get a temp filename");
+        }
+        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("Could not open the temp file: '" << m_buffer << "' because: " << buffer);
+        }
+    }
+#else
+    TempFile::TempFile() {
+        m_file = std::tmpfile();
+        if (!m_file) {
+            CATCH_RUNTIME_ERROR("Could not create a temp file.");
+        }
+    }
+
+#endif
+
+    TempFile::~TempFile() {
+         // TBD: What to do about errors here?
+         std::fclose(m_file);
+         // We manually create the file on Windows only, on Linux
+         // it will be autodeleted
+#if defined(_MSC_VER)
+         std::remove(m_buffer);
+#endif
+    }
+
+    FILE* TempFile::getFile() {
+        return m_file;
+    }
+
+    std::string TempFile::getContents() {
+        std::stringstream sstr;
+        char buffer[100] = {};
+        std::rewind(m_file);
+        while (std::fgets(buffer, sizeof(buffer), m_file)) {
+            sstr << buffer;
+        }
+        return sstr.str();
+    }
+
+    OutputRedirect::OutputRedirect(std::string& stdout_dest, std::string& stderr_dest) :
+        m_originalStdout(dup(1)),
+        m_originalStderr(dup(2)),
+        m_stdoutDest(stdout_dest),
+        m_stderrDest(stderr_dest) {
+        dup2(fileno(m_stdoutFile.getFile()), 1);
+        dup2(fileno(m_stderrFile.getFile()), 2);
+    }
+
+    OutputRedirect::~OutputRedirect() {
+        Catch::cout() << std::flush;
+        fflush(stdout);
+        // Since we support overriding these streams, we flush cerr
+        // even though std::cerr is unbuffered
+        Catch::cerr() << std::flush;
+        Catch::clog() << std::flush;
+        fflush(stderr);
+
+        dup2(m_originalStdout, 1);
+        dup2(m_originalStderr, 2);
+
+        m_stdoutDest += m_stdoutFile.getContents();
+        m_stderrDest += m_stderrFile.getContents();
+    }
+
+#endif // CATCH_CONFIG_NEW_CAPTURE
+
+} // namespace Catch
+
+#if defined(CATCH_CONFIG_NEW_CAPTURE)
+    #if defined(_MSC_VER)
+    #undef dup
+    #undef dup2
+    #undef fileno
+    #endif
+#endif
+// end catch_output_redirect.cpp
+// start catch_polyfills.cpp
+
+#include <cmath>
+
+namespace Catch {
+
+#if !defined(CATCH_CONFIG_POLYFILL_ISNAN)
+    bool isnan(float f) {
+        return std::isnan(f);
+    }
+    bool isnan(double d) {
+        return std::isnan(d);
+    }
+#else
+    // For now we only use this for embarcadero
+    bool isnan(float f) {
+        return std::_isnan(f);
+    }
+    bool isnan(double d) {
+        return std::_isnan(d);
+    }
+#endif
+
+} // end namespace Catch
+// end catch_polyfills.cpp
+// start catch_random_number_generator.cpp
+
+namespace Catch {
+
+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 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)());
+        }
+    }
+
+    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
+// start catch_registry_hub.cpp
+
+// start catch_test_case_registry_impl.h
+
+#include <vector>
+#include <set>
+#include <algorithm>
+#include <ios>
+
+namespace Catch {
+
+    class TestCase;
+    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 );
+
+    std::vector<TestCase> filterTests( std::vector<TestCase> const& testCases, TestSpec const& testSpec, IConfig const& config );
+    std::vector<TestCase> const& getAllTestCasesSorted( IConfig const& config );
+
+    class TestRegistry : public ITestCaseRegistry {
+    public:
+        virtual ~TestRegistry() = default;
+
+        virtual void registerTest( TestCase const& testCase );
+
+        std::vector<TestCase> const& getAllTests() const override;
+        std::vector<TestCase> const& getAllTestsSorted( IConfig const& config ) const override;
+
+    private:
+        std::vector<TestCase> m_functions;
+        mutable RunTests::InWhatOrder m_currentSortOrder = RunTests::InDeclarationOrder;
+        mutable std::vector<TestCase> m_sortedFunctions;
+        std::size_t m_unnamedCount = 0;
+        std::ios_base::Init m_ostreamInit; // Forces cout/ cerr to be initialised
+    };
+
+    ///////////////////////////////////////////////////////////////////////////
+
+    class TestInvokerAsFunction : public ITestInvoker {
+        void(*m_testAsFunction)();
+    public:
+        TestInvokerAsFunction( void(*testAsFunction)() ) noexcept;
+
+        void invoke() const override;
+    };
+
+    std::string extractClassName( StringRef const& classOrQualifiedMethodName );
+
+    ///////////////////////////////////////////////////////////////////////////
+
+} // end namespace Catch
+
+// end catch_test_case_registry_impl.h
+// start catch_reporter_registry.h
+
+#include <map>
+
+namespace Catch {
+
+    class ReporterRegistry : public IReporterRegistry {
+
+    public:
+
+        ~ReporterRegistry() override;
+
+        IStreamingReporterPtr create( std::string const& name, IConfigPtr const& config ) const override;
+
+        void registerReporter( std::string const& name, IReporterFactoryPtr const& factory );
+        void registerListener( IReporterFactoryPtr const& factory );
+
+        FactoryMap const& getFactories() const override;
+        Listeners const& getListeners() const override;
+
+    private:
+        FactoryMap m_factories;
+        Listeners m_listeners;
+    };
+}
+
+// end catch_reporter_registry.h
+// start catch_tag_alias_registry.h
+
+// start catch_tag_alias.h
+
+#include <string>
+
+namespace Catch {
+
+    struct TagAlias {
+        TagAlias(std::string const& _tag, SourceLineInfo _lineInfo);
+
+        std::string tag;
+        SourceLineInfo lineInfo;
+    };
+
+} // end namespace Catch
+
+// end catch_tag_alias.h
+#include <map>
+
+namespace Catch {
+
+    class TagAliasRegistry : public ITagAliasRegistry {
+    public:
+        ~TagAliasRegistry() override;
+        TagAlias const* find( std::string const& alias ) const override;
+        std::string expandAliases( std::string const& unexpandedTestSpec ) const override;
+        void add( std::string const& alias, std::string const& tag, SourceLineInfo const& lineInfo );
+
+    private:
+        std::map<std::string, TagAlias> m_registry;
+    };
+
+} // end namespace Catch
+
+// end catch_tag_alias_registry.h
+// start catch_startup_exception_registry.h
+
+#include <vector>
+#include <exception>
+
+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
+
+// end catch_startup_exception_registry.h
+// start catch_singletons.hpp
+
+namespace Catch {
+
+    struct ISingleton {
+        virtual ~ISingleton();
+    };
+
+    void addSingleton( ISingleton* singleton );
+    void cleanupSingletons();
+
+    template<typename SingletonImplT, typename InterfaceT = SingletonImplT, typename MutableInterfaceT = InterfaceT>
+    class Singleton : SingletonImplT, public ISingleton {
+
+        static auto getInternal() -> Singleton* {
+            static Singleton* s_instance = nullptr;
+            if( !s_instance ) {
+                s_instance = new Singleton;
+                addSingleton( s_instance );
+            }
+            return s_instance;
+        }
+
+    public:
+        static auto get() -> InterfaceT const& {
+            return *getInternal();
+        }
+        static auto getMutable() -> MutableInterfaceT& {
+            return *getInternal();
+        }
+    };
+
+} // namespace Catch
+
+// end catch_singletons.hpp
+namespace Catch {
+
+    namespace {
+
+        class RegistryHub : public IRegistryHub, public IMutableRegistryHub,
+                            private NonCopyable {
+
+        public: // IRegistryHub
+            RegistryHub() = default;
+            IReporterRegistry const& getReporterRegistry() const override {
+                return m_reporterRegistry;
+            }
+            ITestCaseRegistry const& getTestCaseRegistry() const override {
+                return m_testCaseRegistry;
+            }
+            IExceptionTranslatorRegistry const& getExceptionTranslatorRegistry() const override {
+                return m_exceptionTranslatorRegistry;
+            }
+            ITagAliasRegistry const& getTagAliasRegistry() const override {
+                return m_tagAliasRegistry;
+            }
+            StartupExceptionRegistry const& getStartupExceptionRegistry() const override {
+                return m_exceptionRegistry;
+            }
+
+        public: // IMutableRegistryHub
+            void registerReporter( std::string const& name, IReporterFactoryPtr const& factory ) override {
+                m_reporterRegistry.registerReporter( name, factory );
+            }
+            void registerListener( IReporterFactoryPtr const& factory ) override {
+                m_reporterRegistry.registerListener( factory );
+            }
+            void registerTest( TestCase const& testInfo ) override {
+                m_testCaseRegistry.registerTest( testInfo );
+            }
+            void registerTranslator( const IExceptionTranslator* translator ) override {
+                m_exceptionTranslatorRegistry.registerTranslator( translator );
+            }
+            void registerTagAlias( std::string const& alias, std::string const& tag, SourceLineInfo const& lineInfo ) override {
+                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:
+            TestRegistry m_testCaseRegistry;
+            ReporterRegistry m_reporterRegistry;
+            ExceptionTranslatorRegistry m_exceptionTranslatorRegistry;
+            TagAliasRegistry m_tagAliasRegistry;
+            StartupExceptionRegistry m_exceptionRegistry;
+            Detail::EnumValuesRegistry m_enumValuesRegistry;
+        };
+    }
+
+    using RegistryHubSingleton = Singleton<RegistryHub, IRegistryHub, IMutableRegistryHub>;
+
+    IRegistryHub const& getRegistryHub() {
+        return RegistryHubSingleton::get();
+    }
+    IMutableRegistryHub& getMutableRegistryHub() {
+        return RegistryHubSingleton::getMutable();
+    }
+    void cleanUp() {
+        cleanupSingletons();
+        cleanUpContext();
+    }
+    std::string translateActiveException() {
+        return getRegistryHub().getExceptionTranslatorRegistry().translateActiveException();
+    }
+
+} // end namespace Catch
+// end catch_registry_hub.cpp
+// start catch_reporter_registry.cpp
+
+namespace Catch {
+
+    ReporterRegistry::~ReporterRegistry() = default;
+
+    IStreamingReporterPtr ReporterRegistry::create( std::string const& name, IConfigPtr const& config ) const {
+        auto it =  m_factories.find( name );
+        if( it == m_factories.end() )
+            return nullptr;
+        return it->second->create( ReporterConfig( config ) );
+    }
+
+    void ReporterRegistry::registerReporter( std::string const& name, IReporterFactoryPtr const& factory ) {
+        m_factories.emplace(name, factory);
+    }
+    void ReporterRegistry::registerListener( IReporterFactoryPtr const& factory ) {
+        m_listeners.push_back( factory );
+    }
+
+    IReporterRegistry::FactoryMap const& ReporterRegistry::getFactories() const {
+        return m_factories;
+    }
+    IReporterRegistry::Listeners const& ReporterRegistry::getListeners() const {
+        return m_listeners;
+    }
+
+}
+// end catch_reporter_registry.cpp
+// start catch_result_type.cpp
+
+namespace Catch {
+
+    bool isOk( ResultWas::OfType resultType ) {
+        return ( resultType & ResultWas::FailureBit ) == 0;
+    }
+    bool isJustInfo( int flags ) {
+        return flags == ResultWas::Info;
+    }
+
+    ResultDisposition::Flags operator | ( ResultDisposition::Flags lhs, ResultDisposition::Flags rhs ) {
+        return static_cast<ResultDisposition::Flags>( static_cast<int>( lhs ) | static_cast<int>( rhs ) );
+    }
+
+    bool shouldContinueOnFailure( int flags )    { return ( flags & ResultDisposition::ContinueOnFailure ) != 0; }
+    bool shouldSuppressFailure( int flags )      { return ( flags & ResultDisposition::SuppressFail ) != 0; }
+
+} // end namespace Catch
+// end catch_result_type.cpp
+// start catch_run_context.cpp
+
+#include <cassert>
+#include <algorithm>
+#include <sstream>
+
+namespace Catch {
+
+    namespace Generators {
+        struct GeneratorTracker : TestCaseTracking::TrackerBase, IGeneratorTracker {
+            GeneratorBasePtr m_generator;
+
+            GeneratorTracker( TestCaseTracking::NameAndLocation const& nameAndLocation, TrackerContext& ctx, ITracker* parent )
+            :   TrackerBase( nameAndLocation, ctx, parent )
+            {}
+            ~GeneratorTracker();
+
+            static GeneratorTracker& acquire( TrackerContext& ctx, TestCaseTracking::NameAndLocation const& nameAndLocation ) {
+                std::shared_ptr<GeneratorTracker> tracker;
+
+                ITracker& currentTracker = ctx.currentTracker();
+                // 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->isGeneratorTracker() );
+                    tracker = std::static_pointer_cast<GeneratorTracker>( childTracker );
+                } else {
+                    tracker = std::make_shared<GeneratorTracker>( nameAndLocation, ctx, &currentTracker );
+                    currentTracker.addChild( tracker );
+                }
+
+                if( !tracker->isComplete() ) {
+                    tracker->open();
+                }
+
+                return *tracker;
+            }
+
+            // TrackerBase interface
+            bool isGeneratorTracker() const override { return true; }
+            auto hasGenerator() const -> bool override {
+                return !!m_generator;
+            }
+            void close() override {
+                TrackerBase::close();
+                // 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
+            auto getGenerator() const -> GeneratorBasePtr const& override {
+                return m_generator;
+            }
+            void setGenerator( GeneratorBasePtr&& generator ) override {
+                m_generator = std::move( generator );
+            }
+        };
+        GeneratorTracker::~GeneratorTracker() {}
+    }
+
+    RunContext::RunContext(IConfigPtr const& _config, IStreamingReporterPtr&& reporter)
+    :   m_runInfo(_config->name()),
+        m_context(getCurrentMutableContext()),
+        m_config(_config),
+        m_reporter(std::move(reporter)),
+        m_lastAssertionInfo{ StringRef(), SourceLineInfo("",0), StringRef(), ResultDisposition::Normal },
+        m_includeSuccessfulResults( m_config->includeSuccessfulResults() || m_reporter->getPreferences().shouldReportAllAssertions )
+    {
+        m_context.setRunner(this);
+        m_context.setConfig(m_config);
+        m_context.setResultCapture(this);
+        m_reporter->testRunStarting(m_runInfo);
+    }
+
+    RunContext::~RunContext() {
+        m_reporter->testRunEnded(TestRunStats(m_runInfo, m_totals, aborting()));
+    }
+
+    void RunContext::testGroupStarting(std::string const& testSpec, std::size_t groupIndex, std::size_t groupsCount) {
+        m_reporter->testGroupStarting(GroupInfo(testSpec, groupIndex, groupsCount));
+    }
+
+    void RunContext::testGroupEnded(std::string const& testSpec, Totals const& totals, std::size_t groupIndex, std::size_t groupsCount) {
+        m_reporter->testGroupEnded(TestGroupStats(GroupInfo(testSpec, groupIndex, groupsCount), totals, aborting()));
+    }
+
+    Totals RunContext::runTest(TestCase const& testCase) {
+        Totals prevTotals = m_totals;
+
+        std::string redirectedCout;
+        std::string redirectedCerr;
+
+        auto const& testInfo = testCase.getTestCaseInfo();
+
+        m_reporter->testCaseStarting(testInfo);
+
+        m_activeTestCase = &testCase;
+
+        ITracker& rootTracker = m_trackerContext.startRun();
+        assert(rootTracker.isSectionTracker());
+        static_cast<SectionTracker&>(rootTracker).addInitialFilters(m_config->getSectionsToRun());
+        do {
+            m_trackerContext.startCycle();
+            m_testCaseTracker = &SectionTracker::acquire(m_trackerContext, TestCaseTracking::NameAndLocation(testInfo.name, testInfo.lineInfo));
+            runCurrentTest(redirectedCout, redirectedCerr);
+        } while (!m_testCaseTracker->isSuccessfullyCompleted() && !aborting());
+
+        Totals deltaTotals = m_totals.delta(prevTotals);
+        if (testInfo.expectedToFail() && deltaTotals.testCases.passed > 0) {
+            deltaTotals.assertions.failed++;
+            deltaTotals.testCases.passed--;
+            deltaTotals.testCases.failed++;
+        }
+        m_totals.testCases += deltaTotals.testCases;
+        m_reporter->testCaseEnded(TestCaseStats(testInfo,
+                                  deltaTotals,
+                                  redirectedCout,
+                                  redirectedCerr,
+                                  aborting()));
+
+        m_activeTestCase = nullptr;
+        m_testCaseTracker = nullptr;
+
+        return deltaTotals;
+    }
+
+    IConfigPtr RunContext::config() const {
+        return m_config;
+    }
+
+    IStreamingReporter& RunContext::reporter() const {
+        return *m_reporter;
+    }
+
+    void RunContext::assertionEnded(AssertionResult const & result) {
+        if (result.getResultType() == ResultWas::Ok) {
+            m_totals.assertions.passed++;
+            m_lastAssertionPassed = true;
+        } else if (!result.isOk()) {
+            m_lastAssertionPassed = false;
+            if( m_activeTestCase->getTestCaseInfo().okToFail() )
+                m_totals.assertions.failedButOk++;
+            else
+                m_totals.assertions.failed++;
+        }
+        else {
+            m_lastAssertionPassed = true;
+        }
+
+        // We have no use for the return value (whether messages should be cleared), because messages were made scoped
+        // and should be let to clear themselves out.
+        static_cast<void>(m_reporter->assertionEnded(AssertionStats(result, m_messages, m_totals)));
+
+        if (result.getResultType() != ResultWas::Warning)
+            m_messageScopes.clear();
+
+        // Reset working state
+        resetAssertionInfo();
+        m_lastResult = result;
+    }
+    void RunContext::resetAssertionInfo() {
+        m_lastAssertionInfo.macroName = StringRef();
+        m_lastAssertionInfo.capturedExpression = "{Unknown expression after the reported line}"_sr;
+    }
+
+    bool RunContext::sectionStarted(SectionInfo const & sectionInfo, Counts & assertions) {
+        ITracker& sectionTracker = SectionTracker::acquire(m_trackerContext, TestCaseTracking::NameAndLocation(sectionInfo.name, sectionInfo.lineInfo));
+        if (!sectionTracker.isOpen())
+            return false;
+        m_activeSections.push_back(&sectionTracker);
+
+        m_lastAssertionInfo.lineInfo = sectionInfo.lineInfo;
+
+        m_reporter->sectionStarting(sectionInfo);
+
+        assertions = m_totals.assertions;
+
+        return true;
+    }
+    auto RunContext::acquireGeneratorTracker( StringRef generatorName, SourceLineInfo const& lineInfo ) -> IGeneratorTracker& {
+        using namespace Generators;
+        GeneratorTracker& tracker = GeneratorTracker::acquire(m_trackerContext,
+                                                              TestCaseTracking::NameAndLocation( static_cast<std::string>(generatorName), lineInfo ) );
+        m_lastAssertionInfo.lineInfo = lineInfo;
+        return tracker;
+    }
+
+    bool RunContext::testForMissingAssertions(Counts& assertions) {
+        if (assertions.total() != 0)
+            return false;
+        if (!m_config->warnAboutMissingAssertions())
+            return false;
+        if (m_trackerContext.currentTracker().hasChildren())
+            return false;
+        m_totals.assertions.failed++;
+        assertions.failed++;
+        return true;
+    }
+
+    void RunContext::sectionEnded(SectionEndInfo const & endInfo) {
+        Counts assertions = m_totals.assertions - endInfo.prevAssertions;
+        bool missingAssertions = testForMissingAssertions(assertions);
+
+        if (!m_activeSections.empty()) {
+            m_activeSections.back()->close();
+            m_activeSections.pop_back();
+        }
+
+        m_reporter->sectionEnded(SectionStats(endInfo.sectionInfo, assertions, endInfo.durationInSeconds, missingAssertions));
+        m_messages.clear();
+        m_messageScopes.clear();
+    }
+
+    void RunContext::sectionEndedEarly(SectionEndInfo const & endInfo) {
+        if (m_unfinishedSections.empty())
+            m_activeSections.back()->fail();
+        else
+            m_activeSections.back()->close();
+        m_activeSections.pop_back();
+
+        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 ) {
+        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);
+    }
+
+    void RunContext::popScopedMessage(MessageInfo const & message) {
+        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
+            : std::string();
+    }
+
+    const AssertionResult * RunContext::getLastResult() const {
+        return &(*m_lastResult);
+    }
+
+    void RunContext::exceptionEarlyReported() {
+        m_shouldReportUnexpected = false;
+    }
+
+    void RunContext::handleFatalErrorCondition( StringRef message ) {
+        // First notify reporter that bad things happened
+        m_reporter->fatalErrorEncountered(message);
+
+        // 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 = static_cast<std::string>(message);
+        AssertionResult result(m_lastAssertionInfo, tempResult);
+
+        assertionEnded(result);
+
+        handleUnfinishedSections();
+
+        // Recreate section for test case (as we will lose the one that was in scope)
+        auto const& testCaseInfo = m_activeTestCase->getTestCaseInfo();
+        SectionInfo testCaseSection(testCaseInfo.lineInfo, testCaseInfo.name);
+
+        Counts assertions;
+        assertions.failed = 1;
+        SectionStats testCaseSectionStats(testCaseSection, assertions, 0, false);
+        m_reporter->sectionEnded(testCaseSectionStats);
+
+        auto const& testInfo = m_activeTestCase->getTestCaseInfo();
+
+        Totals deltaTotals;
+        deltaTotals.testCases.failed = 1;
+        deltaTotals.assertions.failed = 1;
+        m_reporter->testCaseEnded(TestCaseStats(testInfo,
+                                  deltaTotals,
+                                  std::string(),
+                                  std::string(),
+                                  false));
+        m_totals.testCases.failed++;
+        testGroupEnded(std::string(), m_totals, 1, 1);
+        m_reporter->testRunEnded(TestRunStats(m_runInfo, m_totals, false));
+    }
+
+    bool RunContext::lastAssertionPassed() {
+         return m_lastAssertionPassed;
+    }
+
+    void RunContext::assertionPassed() {
+        m_lastAssertionPassed = true;
+        ++m_totals.assertions.passed;
+        resetAssertionInfo();
+        m_messageScopes.clear();
+    }
+
+    bool RunContext::aborting() const {
+        return m_totals.assertions.failed >= static_cast<std::size_t>(m_config->abortAfter());
+    }
+
+    void RunContext::runCurrentTest(std::string & redirectedCout, std::string & redirectedCerr) {
+        auto const& testCaseInfo = m_activeTestCase->getTestCaseInfo();
+        SectionInfo testCaseSection(testCaseInfo.lineInfo, testCaseInfo.name);
+        m_reporter->sectionStarting(testCaseSection);
+        Counts prevAssertions = m_totals.assertions;
+        double duration = 0;
+        m_shouldReportUnexpected = true;
+        m_lastAssertionInfo = { "TEST_CASE"_sr, testCaseInfo.lineInfo, StringRef(), ResultDisposition::Normal };
+
+        seedRng(*m_config);
+
+        Timer timer;
+        CATCH_TRY {
+            if (m_reporter->getPreferences().shouldRedirectStdOut) {
+#if !defined(CATCH_CONFIG_EXPERIMENTAL_REDIRECT)
+                RedirectedStreams redirectedStreams(redirectedCout, redirectedCerr);
+
+                timer.start();
+                invokeActiveTestCase();
+#else
+                OutputRedirect r(redirectedCout, redirectedCerr);
+                timer.start();
+                invokeActiveTestCase();
+#endif
+            } else {
+                timer.start();
+                invokeActiveTestCase();
+            }
+            duration = timer.getElapsedSeconds();
+        } CATCH_CATCH_ANON (TestFailureException&) {
+            // This just means the test was aborted due to failure
+        } CATCH_CATCH_ALL {
+            // Under CATCH_CONFIG_FAST_COMPILE, unexpected exceptions under REQUIRE assertions
+            // are reported without translation at the point of origin.
+            if( m_shouldReportUnexpected ) {
+                AssertionReaction dummyReaction;
+                handleUnexpectedInflightException( m_lastAssertionInfo, translateActiveException(), dummyReaction );
+            }
+        }
+        Counts assertions = m_totals.assertions - prevAssertions;
+        bool missingAssertions = testForMissingAssertions(assertions);
+
+        m_testCaseTracker->close();
+        handleUnfinishedSections();
+        m_messages.clear();
+        m_messageScopes.clear();
+
+        SectionStats testCaseSectionStats(testCaseSection, assertions, duration, missingAssertions);
+        m_reporter->sectionEnded(testCaseSectionStats);
+    }
+
+    void RunContext::invokeActiveTestCase() {
+        FatalConditionHandlerGuard _(&m_fatalConditionhandler);
+        m_activeTestCase->invoke();
+    }
+
+    void RunContext::handleUnfinishedSections() {
+        // If sections ended prematurely due to an exception we stored their
+        // infos here so we can tear them down outside the unwind process.
+        for (auto it = m_unfinishedSections.rbegin(),
+             itEnd = m_unfinishedSections.rend();
+             it != itEnd;
+             ++it)
+            sectionEnded(*it);
+        m_unfinishedSections.clear();
+    }
+
+    void RunContext::handleExpr(
+        AssertionInfo const& info,
+        ITransientExpression const& expr,
+        AssertionReaction& reaction
+    ) {
+        m_reporter->assertionStarting( info );
+
+        bool negated = isFalseTest( info.resultDisposition );
+        bool result = expr.getResult() != negated;
+
+        if( result ) {
+            if (!m_includeSuccessfulResults) {
+                assertionPassed();
+            }
+            else {
+                reportExpr(info, ResultWas::Ok, &expr, negated);
+            }
+        }
+        else {
+            reportExpr(info, ResultWas::ExpressionFailed, &expr, negated );
+            populateReaction( reaction );
+        }
+    }
+    void RunContext::reportExpr(
+            AssertionInfo const &info,
+            ResultWas::OfType resultType,
+            ITransientExpression const *expr,
+            bool negated ) {
+
+        m_lastAssertionInfo = info;
+        AssertionResultData data( resultType, LazyExpression( negated ) );
+
+        AssertionResult assertionResult{ info, data };
+        assertionResult.m_resultData.lazyExpression.m_transientExpression = expr;
+
+        assertionEnded( assertionResult );
+    }
+
+    void RunContext::handleMessage(
+            AssertionInfo const& info,
+            ResultWas::OfType resultType,
+            StringRef const& message,
+            AssertionReaction& reaction
+    ) {
+        m_reporter->assertionStarting( info );
+
+        m_lastAssertionInfo = info;
+
+        AssertionResultData data( resultType, LazyExpression( false ) );
+        data.message = static_cast<std::string>(message);
+        AssertionResult assertionResult{ m_lastAssertionInfo, data };
+        assertionEnded( assertionResult );
+        if( !assertionResult.isOk() )
+            populateReaction( reaction );
+    }
+    void RunContext::handleUnexpectedExceptionNotThrown(
+            AssertionInfo const& info,
+            AssertionReaction& reaction
+    ) {
+        handleNonExpr(info, Catch::ResultWas::DidntThrowException, reaction);
+    }
+
+    void RunContext::handleUnexpectedInflightException(
+            AssertionInfo const& info,
+            std::string const& message,
+            AssertionReaction& reaction
+    ) {
+        m_lastAssertionInfo = info;
+
+        AssertionResultData data( ResultWas::ThrewException, LazyExpression( false ) );
+        data.message = message;
+        AssertionResult assertionResult{ info, data };
+        assertionEnded( assertionResult );
+        populateReaction( reaction );
+    }
+
+    void RunContext::populateReaction( AssertionReaction& reaction ) {
+        reaction.shouldDebugBreak = m_config->shouldDebugBreak();
+        reaction.shouldThrow = aborting() || (m_lastAssertionInfo.resultDisposition & ResultDisposition::Normal);
+    }
+
+    void RunContext::handleIncomplete(
+            AssertionInfo const& info
+    ) {
+        m_lastAssertionInfo = info;
+
+        AssertionResultData data( ResultWas::ThrewException, LazyExpression( false ) );
+        data.message = "Exception translation was disabled by CATCH_CONFIG_FAST_COMPILE";
+        AssertionResult assertionResult{ info, data };
+        assertionEnded( assertionResult );
+    }
+    void RunContext::handleNonExpr(
+            AssertionInfo const &info,
+            ResultWas::OfType resultType,
+            AssertionReaction &reaction
+    ) {
+        m_lastAssertionInfo = info;
+
+        AssertionResultData data( resultType, LazyExpression( false ) );
+        AssertionResult assertionResult{ info, data };
+        assertionEnded( assertionResult );
+
+        if( !assertionResult.isOk() )
+            populateReaction( reaction );
+    }
+
+    IResultCapture& getResultCapture() {
+        if (auto* capture = getCurrentContext().getResultCapture())
+            return *capture;
+        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
+
+namespace Catch {
+
+    Section::Section( SectionInfo const& info )
+    :   m_info( info ),
+        m_sectionIncluded( getResultCapture().sectionStarted( m_info, m_assertions ) )
+    {
+        m_timer.start();
+    }
+
+    Section::~Section() {
+        if( m_sectionIncluded ) {
+            SectionEndInfo endInfo{ m_info, m_assertions, m_timer.getElapsedSeconds() };
+            if( uncaught_exceptions() )
+                getResultCapture().sectionEndedEarly( endInfo );
+            else
+                getResultCapture().sectionEnded( endInfo );
+        }
+    }
+
+    // This indicates whether the section should be executed or not
+    Section::operator bool() const {
+        return m_sectionIncluded;
+    }
+
+} // end namespace Catch
+// end catch_section.cpp
+// start catch_section_info.cpp
+
+namespace Catch {
+
+    SectionInfo::SectionInfo
+        (   SourceLineInfo const& _lineInfo,
+            std::string const& _name )
+    :   name( _name ),
+        lineInfo( _lineInfo )
+    {}
+
+} // end namespace Catch
+// end catch_section_info.cpp
+// start catch_session.cpp
+
+// start catch_session.h
+
+#include <memory>
+
+namespace Catch {
+
+    class Session : NonCopyable {
+    public:
+
+        Session();
+        ~Session() override;
+
+        void showHelp() const;
+        void libIdentify();
+
+        int applyCommandLine( int argc, char const * const * argv );
+    #if defined(CATCH_CONFIG_WCHAR) && defined(_WIN32) && defined(UNICODE)
+        int applyCommandLine( int argc, wchar_t const * const * argv );
+    #endif
+
+        void useConfigData( ConfigData const& configData );
+
+        template<typename CharT>
+        int run(int argc, CharT const * const argv[]) {
+            if (m_startupExceptions)
+                return 1;
+            int returnCode = applyCommandLine(argc, argv);
+            if (returnCode == 0)
+                returnCode = run();
+            return returnCode;
+        }
+
+        int run();
+
+        clara::Parser const& cli() const;
+        void cli( clara::Parser const& newParser );
+        ConfigData& configData();
+        Config& config();
+    private:
+        int runInternal();
+
+        clara::Parser m_cli;
+        ConfigData m_configData;
+        std::shared_ptr<Config> m_config;
+        bool m_startupExceptions = false;
+    };
+
+} // end namespace Catch
+
+// end catch_session.h
+// start catch_version.h
+
+#include <iosfwd>
+
+namespace Catch {
+
+    // Versioning information
+    struct Version {
+        Version( Version const& ) = delete;
+        Version& operator=( Version const& ) = delete;
+        Version(    unsigned int _majorVersion,
+                    unsigned int _minorVersion,
+                    unsigned int _patchNumber,
+                    char const * const _branchName,
+                    unsigned int _buildNumber );
+
+        unsigned int const majorVersion;
+        unsigned int const minorVersion;
+        unsigned int const patchNumber;
+
+        // buildNumber is only used if branchName is not null
+        char const * const branchName;
+        unsigned int const buildNumber;
+
+        friend std::ostream& operator << ( std::ostream& os, Version const& version );
+    };
+
+    Version const& libraryVersion();
+}
+
+// end catch_version.h
+#include <cstdlib>
+#include <iomanip>
+#include <set>
+#include <iterator>
+
+namespace Catch {
+
+    namespace {
+        const int MaxExitCode = 255;
+
+        IStreamingReporterPtr createReporter(std::string const& reporterName, IConfigPtr const& config) {
+            auto reporter = Catch::getRegistryHub().getReporterRegistry().create(reporterName, config);
+            CATCH_ENFORCE(reporter, "No reporter registered with name: '" << reporterName << "'");
+
+            return reporter;
+        }
+
+        IStreamingReporterPtr makeReporter(std::shared_ptr<Config> const& config) {
+            if (Catch::getRegistryHub().getReporterRegistry().getListeners().empty()) {
+                return createReporter(config->getReporterName(), config);
+            }
+
+            // 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.addReporter(createReporter(config->getReporterName(), config));
+            return ret;
+        }
+
+        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());
+                }
+            }
+
+            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);
+                }
+
+                for (auto const& match : m_matches) {
+                    if (match.tests.empty()) {
+                        m_context.reporter().noMatchingTestCases(match.name);
+                        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;
+            }
+
+        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));
+            for (auto& testCase : tests) {
+                auto tags = testCase.tags;
+
+                std::string filename = testCase.lineInfo.file;
+                auto lastSlash = filename.find_last_of("\\/");
+                if (lastSlash != std::string::npos) {
+                    filename.erase(0, lastSlash);
+                    filename[0] = '#';
+                }
+                else
+                {
+                    filename.insert(0, "#");
+                }
+
+                auto lastDot = filename.find_last_of('.');
+                if (lastDot != std::string::npos) {
+                    filename.erase(lastDot);
+                }
+
+                tags.push_back(std::move(filename));
+                setTags(testCase, tags);
+            }
+        }
+
+    } // anon namespace
+
+    Session::Session() {
+        static bool alreadyInstantiated = false;
+        if( alreadyInstantiated ) {
+            CATCH_TRY { CATCH_INTERNAL_ERROR( "Only one instance of Catch::Session can ever be used" ); }
+            CATCH_CATCH_ALL { getMutableRegistryHub().registerStartupException(); }
+        }
+
+        // There cannot be exceptions at startup in no-exception mode.
+#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';
+            // iterate over all exceptions and notify user
+            for ( const auto& ex_ptr : exceptions ) {
+                try {
+                    std::rethrow_exception(ex_ptr);
+                } catch ( std::exception const& ex ) {
+                    Catch::cerr() << Column( ex.what() ).indent(2) << '\n';
+                }
+            }
+        }
+#endif
+
+        alreadyInstantiated = true;
+        m_cli = makeCommandLineParser( m_configData );
+    }
+    Session::~Session() {
+        Catch::cleanUp();
+    }
+
+    void Session::showHelp() const {
+        Catch::cout()
+                << "\nCatch v" << libraryVersion() << "\n"
+                << m_cli << std::endl
+                << "For more detailed usage please see the project docs\n" << std::endl;
+    }
+    void Session::libIdentify() {
+        Catch::cout()
+                << std::left << std::setw(16) << "description: " << "A Catch2 test executable\n"
+                << std::left << std::setw(16) << "category: " << "testframework\n"
+                << std::left << std::setw(16) << "framework: " << "Catch Test\n"
+                << std::left << std::setw(16) << "version: " << libraryVersion() << std::endl;
+    }
+
+    int Session::applyCommandLine( int argc, char const * const * argv ) {
+        if( m_startupExceptions )
+            return 1;
+
+        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"
+                << Column( result.errorMessage() ).indent( 2 )
+                << "\n\n";
+            Catch::cerr() << "Run with -? for usage\n" << std::endl;
+            return MaxExitCode;
+        }
+
+        if( m_configData.showHelp )
+            showHelp();
+        if( m_configData.libIdentify )
+            libIdentify();
+        m_config.reset();
+        return 0;
+    }
+
+#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, nullptr, 0, nullptr, nullptr );
+
+            utf8Argv[ i ] = new char[ bufSize ];
+
+            WideCharToMultiByte( CP_UTF8, 0, argv[i], -1, utf8Argv[i], bufSize, nullptr, nullptr );
+        }
+
+        int returnCode = applyCommandLine( argc, utf8Argv );
+
+        for ( int i = 0; i < argc; ++i )
+            delete [] utf8Argv[ i ];
+
+        delete [] utf8Argv;
+
+        return returnCode;
+    }
+#endif
+
+    void Session::useConfigData( ConfigData const& configData ) {
+        m_configData = configData;
+        m_config.reset();
+    }
+
+    int Session::run() {
+        if( ( m_configData.waitForKeypress & WaitForKeypress::BeforeStart ) != 0 ) {
+            Catch::cout() << "...waiting for enter/ return before starting" << std::endl;
+            static_cast<void>(std::getchar());
+        }
+        int exitCode = runInternal();
+        if( ( m_configData.waitForKeypress & WaitForKeypress::BeforeExit ) != 0 ) {
+            Catch::cout() << "...waiting for enter/ return before exiting, with code: " << exitCode << std::endl;
+            static_cast<void>(std::getchar());
+        }
+        return exitCode;
+    }
+
+    clara::Parser const& Session::cli() const {
+        return m_cli;
+    }
+    void Session::cli( clara::Parser const& newParser ) {
+        m_cli = newParser;
+    }
+    ConfigData& Session::configData() {
+        return m_configData;
+    }
+    Config& Session::config() {
+        if( !m_config )
+            m_config = std::make_shared<Config>( m_configData );
+        return *m_config;
+    }
+
+    int Session::runInternal() {
+        if( m_startupExceptions )
+            return 1;
+
+        if (m_configData.showHelp || m_configData.libIdentify) {
+            return 0;
+        }
+
+        CATCH_TRY {
+            config(); // Force config to be constructed
+
+            seedRng( *m_config );
+
+            if( m_configData.filenamesAsTags )
+                applyFilenamesAsTags( *m_config );
+
+            // Handle list request
+            if( Option<std::size_t> listed = list( m_config ) )
+                return (std::min) (MaxExitCode, static_cast<int>(*listed));
+
+            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
+            return (std::min) (MaxExitCode, (std::max) (totals.error, static_cast<int>(totals.assertions.failed)));
+        }
+#if !defined(CATCH_CONFIG_DISABLE_EXCEPTIONS)
+        catch( std::exception& ex ) {
+            Catch::cerr() << ex.what() << std::endl;
+            return MaxExitCode;
+        }
+#endif
+    }
+
+} // end namespace Catch
+// end catch_session.cpp
+// start catch_singletons.cpp
+
+#include <vector>
+
+namespace Catch {
+
+    namespace {
+        static auto getSingletons() -> std::vector<ISingleton*>*& {
+            static std::vector<ISingleton*>* g_singletons = nullptr;
+            if( !g_singletons )
+                g_singletons = new std::vector<ISingleton*>();
+            return g_singletons;
+        }
+    }
+
+    ISingleton::~ISingleton() {}
+
+    void addSingleton(ISingleton* singleton ) {
+        getSingletons()->push_back( singleton );
+    }
+    void cleanupSingletons() {
+        auto& singletons = getSingletons();
+        for( auto singleton : *singletons )
+            delete singleton;
+        delete singletons;
+        singletons = nullptr;
+    }
+
+} // namespace Catch
+// 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 {
+            m_exceptions.push_back(exception);
+        } CATCH_CATCH_ALL {
+            // If we run out of memory during start-up there's really not a lot more we can do about it
+            std::terminate();
+        }
+    }
+
+    std::vector<std::exception_ptr> const& StartupExceptionRegistry::getExceptions() const noexcept {
+        return m_exceptions;
+    }
+
+} // end namespace Catch
+#endif
+// end catch_startup_exception_registry.cpp
+// start catch_stream.cpp
+
+#include <cstdio>
+#include <iostream>
+#include <fstream>
+#include <sstream>
+#include <vector>
+#include <memory>
+
+namespace Catch {
+
+    Catch::IStream::~IStream() = default;
+
+    namespace Detail { namespace {
+        template<typename WriterF, std::size_t bufferSize=256>
+        class StreamBufImpl : public std::streambuf {
+            char data[bufferSize];
+            WriterF m_writer;
+
+        public:
+            StreamBufImpl() {
+                setp( data, data + sizeof(data) );
+            }
+
+            ~StreamBufImpl() noexcept {
+                StreamBufImpl::sync();
+            }
+
+        private:
+            int overflow( int c ) override {
+                sync();
+
+                if( c != EOF ) {
+                    if( pbase() == epptr() )
+                        m_writer( std::string( 1, static_cast<char>( c ) ) );
+                    else
+                        sputc( static_cast<char>( c ) );
+                }
+                return 0;
+            }
+
+            int sync() override {
+                if( pbase() != pptr() ) {
+                    m_writer( std::string( pbase(), static_cast<std::string::size_type>( pptr() - pbase() ) ) );
+                    setp( pbase(), epptr() );
+                }
+                return 0;
+            }
+        };
+
+        ///////////////////////////////////////////////////////////////////////////
+
+        struct OutputDebugWriter {
+
+            void operator()( std::string const&str ) {
+                writeToDebugConsole( str );
+            }
+        };
+
+        ///////////////////////////////////////////////////////////////////////////
+
+        class FileStream : public IStream {
+            mutable std::ofstream m_ofs;
+        public:
+            FileStream( StringRef filename ) {
+                m_ofs.open( filename.c_str() );
+                CATCH_ENFORCE( !m_ofs.fail(), "Unable to open file: '" << filename << "'" );
+            }
+            ~FileStream() override = default;
+        public: // IStream
+            std::ostream& stream() const override {
+                return m_ofs;
+            }
+        };
+
+        ///////////////////////////////////////////////////////////////////////////
+
+        class CoutStream : public IStream {
+            mutable std::ostream m_os;
+        public:
+            // Store the streambuf from cout up-front because
+            // cout may get redirected when running tests
+            CoutStream() : m_os( Catch::cout().rdbuf() ) {}
+            ~CoutStream() override = default;
+
+        public: // IStream
+            std::ostream& stream() const override { return m_os; }
+        };
+
+        ///////////////////////////////////////////////////////////////////////////
+
+        class DebugOutStream : public IStream {
+            std::unique_ptr<StreamBufImpl<OutputDebugWriter>> m_streamBuf;
+            mutable std::ostream m_os;
+        public:
+            DebugOutStream()
+            :   m_streamBuf( new StreamBufImpl<OutputDebugWriter>() ),
+                m_os( m_streamBuf.get() )
+            {}
+
+            ~DebugOutStream() override = default;
+
+        public: // IStream
+            std::ostream& stream() const override { return m_os; }
+        };
+
+    }} // namespace anon::detail
+
+    ///////////////////////////////////////////////////////////////////////////
+
+    auto makeStream( StringRef const &filename ) -> IStream const* {
+        if( filename.empty() )
+            return new Detail::CoutStream();
+        else if( filename[0] == '%' ) {
+            if( filename == "%debug" )
+                return new Detail::DebugOutStream();
+            else
+                CATCH_ERROR( "Unrecognised stream: '" << filename << "'" );
+        }
+        else
+            return new Detail::FileStream( filename );
+    }
+
+    // This class encapsulates the idea of a pool of ostringstreams that can be reused.
+    struct StringStreams {
+        std::vector<std::unique_ptr<std::ostringstream>> m_streams;
+        std::vector<std::size_t> m_unused;
+        std::ostringstream m_referenceStream; // Used for copy state/ flags from
+
+        auto add() -> std::size_t {
+            if( m_unused.empty() ) {
+                m_streams.push_back( std::unique_ptr<std::ostringstream>( new std::ostringstream ) );
+                return m_streams.size()-1;
+            }
+            else {
+                auto index = m_unused.back();
+                m_unused.pop_back();
+                return index;
+            }
+        }
+
+        void release( std::size_t index ) {
+            m_streams[index]->copyfmt( m_referenceStream ); // Restore initial flags and other state
+            m_unused.push_back(index);
+        }
+    };
+
+    ReusableStringStream::ReusableStringStream()
+    :   m_index( Singleton<StringStreams>::getMutable().add() ),
+        m_oss( Singleton<StringStreams>::getMutable().m_streams[m_index].get() )
+    {}
+
+    ReusableStringStream::~ReusableStringStream() {
+        static_cast<std::ostringstream*>( m_oss )->str("");
+        m_oss->clear();
+        Singleton<StringStreams>::getMutable().release( m_index );
+    }
+
+    auto ReusableStringStream::str() const -> std::string {
+        return static_cast<std::ostringstream*>( m_oss )->str();
+    }
+
+    ///////////////////////////////////////////////////////////////////////////
+
+#ifndef CATCH_CONFIG_NOSTDOUT // If you #define this you must implement these functions
+    std::ostream& cout() { return std::cout; }
+    std::ostream& cerr() { return std::cerr; }
+    std::ostream& clog() { return std::clog; }
+#endif
+}
+// end catch_stream.cpp
+// start catch_string_manip.cpp
+
+#include <algorithm>
+#include <ostream>
+#include <cstring>
+#include <cctype>
+#include <vector>
+
+namespace Catch {
+
+    namespace {
+        char toLowerCh(char c) {
+            return static_cast<char>( std::tolower( static_cast<unsigned char>(c) ) );
+        }
+    }
+
+    bool startsWith( std::string const& s, std::string const& prefix ) {
+        return s.size() >= prefix.size() && std::equal(prefix.begin(), prefix.end(), s.begin());
+    }
+    bool startsWith( std::string const& s, char prefix ) {
+        return !s.empty() && s[0] == prefix;
+    }
+    bool endsWith( std::string const& s, std::string const& suffix ) {
+        return s.size() >= suffix.size() && std::equal(suffix.rbegin(), suffix.rend(), s.rbegin());
+    }
+    bool endsWith( std::string const& s, char suffix ) {
+        return !s.empty() && s[s.size()-1] == suffix;
+    }
+    bool contains( std::string const& s, std::string const& infix ) {
+        return s.find( infix ) != std::string::npos;
+    }
+    void toLowerInPlace( std::string& s ) {
+        std::transform( s.begin(), s.end(), s.begin(), toLowerCh );
+    }
+    std::string toLower( std::string const& s ) {
+        std::string lc = s;
+        toLowerInPlace( lc );
+        return lc;
+    }
+    std::string trim( std::string const& str ) {
+        static char const* whitespaceChars = "\n\r\t ";
+        std::string::size_type start = str.find_first_not_of( whitespaceChars );
+        std::string::size_type end = str.find_last_not_of( whitespaceChars );
+
+        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 );
+        while( i != std::string::npos ) {
+            replaced = true;
+            str = str.substr( 0, i ) + withThis + str.substr( i+replaceThis.size() );
+            if( i < str.size()-withThis.size() )
+                i = str.find( replaceThis, i+withThis.size() );
+            else
+                i = std::string::npos;
+        }
+        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 )
+    {}
+
+    std::ostream& operator << ( std::ostream& os, pluralise const& pluraliser ) {
+        os << pluraliser.m_count << ' ' << pluraliser.m_label;
+        if( pluraliser.m_count != 1 )
+            os << 's';
+        return os;
+    }
+
+}
+// end catch_string_manip.cpp
+// start catch_stringref.cpp
+
+#include <algorithm>
+#include <ostream>
+#include <cstring>
+#include <cstdint>
+
+namespace Catch {
+    StringRef::StringRef( char const* rawChars ) noexcept
+    : StringRef( rawChars, static_cast<StringRef::size_type>(std::strlen(rawChars) ) )
+    {}
+
+    auto StringRef::c_str() const -> char const* {
+        CATCH_ENFORCE(isNullTerminated(), "Called StringRef::c_str() on a non-null-terminated instance");
+        return m_start;
+    }
+    auto StringRef::data() const noexcept -> char const* {
+        return m_start;
+    }
+
+    auto StringRef::substr( size_type start, size_type size ) const noexcept -> StringRef {
+        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 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.data(), str.size());
+    }
+
+    auto operator+=( std::string& lhs, StringRef const& rhs ) -> std::string& {
+        lhs.append(rhs.data(), rhs.size());
+        return lhs;
+    }
+
+} // namespace Catch
+// end catch_stringref.cpp
+// start catch_tag_alias.cpp
+
+namespace Catch {
+    TagAlias::TagAlias(std::string const & _tag, SourceLineInfo _lineInfo): tag(_tag), lineInfo(_lineInfo) {}
+}
+// end catch_tag_alias.cpp
+// start catch_tag_alias_autoregistrar.cpp
+
+namespace Catch {
+
+    RegistrarForTagAliases::RegistrarForTagAliases(char const* alias, char const* tag, SourceLineInfo const& lineInfo) {
+        CATCH_TRY {
+            getMutableRegistryHub().registerTagAlias(alias, tag, lineInfo);
+        } CATCH_CATCH_ALL {
+            // Do not throw when constructing global objects, instead register the exception to be processed later
+            getMutableRegistryHub().registerStartupException();
+        }
     }
 
-    bool AssertionResult::hasExpression() const {
-        return m_info.capturedExpression[0] != 0;
-    }
+}
+// end catch_tag_alias_autoregistrar.cpp
+// start catch_tag_alias_registry.cpp
 
-    bool AssertionResult::hasMessage() const {
-        return !m_resultData.message.empty();
-    }
+#include <sstream>
 
-    std::string capturedExpressionWithSecondArgument( char const * capturedExpression, char const * secondArg ) {
-        return (secondArg[0] == 0 || secondArg[0] == '"' && secondArg[1] == '"')
-            ? capturedExpression
-            : std::string(capturedExpression) + ", " + secondArg;
-    }
+namespace Catch {
 
-    std::string AssertionResult::getExpression() const {
-        if( isFalseTest( m_info.resultDisposition ) )
-            return "!(" + capturedExpressionWithSecondArgument(m_info.capturedExpression, m_info.secondArg) + ")";
-        else
-            return capturedExpressionWithSecondArgument(m_info.capturedExpression, m_info.secondArg);
-    }
-    std::string AssertionResult::getExpressionInMacro() const {
-        if( m_info.macroName[0] == 0 )
-            return capturedExpressionWithSecondArgument(m_info.capturedExpression, m_info.secondArg);
-        else
-            return std::string(m_info.macroName) + "( " + capturedExpressionWithSecondArgument(m_info.capturedExpression, m_info.secondArg) + " )";
-    }
+    TagAliasRegistry::~TagAliasRegistry() {}
 
-    bool AssertionResult::hasExpandedExpression() const {
-        return hasExpression() && getExpandedExpression() != getExpression();
+    TagAlias const* TagAliasRegistry::find( std::string const& alias ) const {
+        auto it = m_registry.find( alias );
+        if( it != m_registry.end() )
+            return &(it->second);
+        else
+            return nullptr;
     }
 
-    std::string AssertionResult::getExpandedExpression() const {
-        return m_resultData.reconstructExpression();
+    std::string TagAliasRegistry::expandAliases( std::string const& unexpandedTestSpec ) const {
+        std::string expandedTestSpec = unexpandedTestSpec;
+        for( auto const& registryKvp : m_registry ) {
+            std::size_t pos = expandedTestSpec.find( registryKvp.first );
+            if( pos != std::string::npos ) {
+                expandedTestSpec =  expandedTestSpec.substr( 0, pos ) +
+                                    registryKvp.second.tag +
+                                    expandedTestSpec.substr( pos + registryKvp.first.size() );
+            }
+        }
+        return expandedTestSpec;
     }
 
-    std::string AssertionResult::getMessage() const {
-        return m_resultData.message;
-    }
-    SourceLineInfo AssertionResult::getSourceInfo() const {
-        return m_info.lineInfo;
-    }
+    void TagAliasRegistry::add( std::string const& alias, std::string const& tag, SourceLineInfo const& lineInfo ) {
+        CATCH_ENFORCE( startsWith(alias, "[@") && endsWith(alias, ']'),
+                      "error: tag alias, '" << alias << "' is not of the form [@alias name].\n" << lineInfo );
 
-    std::string AssertionResult::getTestMacroName() const {
-        return m_info.macroName;
+        CATCH_ENFORCE( m_registry.insert(std::make_pair(alias, TagAlias(tag, lineInfo))).second,
+                      "error: tag alias, '" << alias << "' already registered.\n"
+                      << "\tFirst seen at: " << find(alias)->lineInfo << "\n"
+                      << "\tRedefined at: " << lineInfo );
     }
 
-    void AssertionResult::discardDecomposedExpression() const {
-        m_resultData.decomposedExpression = CATCH_NULL;
-    }
+    ITagAliasRegistry::~ITagAliasRegistry() {}
 
-    void AssertionResult::expandDecomposedExpression() const {
-        m_resultData.reconstructExpression();
+    ITagAliasRegistry const& ITagAliasRegistry::get() {
+        return getRegistryHub().getTagAliasRegistry();
     }
 
 } // end namespace Catch
-
-// #included from: catch_test_case_info.hpp
-#define TWOBLUECUBES_CATCH_TEST_CASE_INFO_HPP_INCLUDED
+// end catch_tag_alias_registry.cpp
+// start catch_test_case_info.cpp
 
 #include <cctype>
+#include <exception>
+#include <algorithm>
+#include <sstream>
 
 namespace Catch {
 
-    inline TestCaseInfo::SpecialProperties parseSpecialTag( std::string const& tag ) {
-        if( startsWith( tag, '.' ) ||
-            tag == "hide" ||
-            tag == "!hide" )
-            return TestCaseInfo::IsHidden;
-        else if( tag == "!throws" )
-            return TestCaseInfo::Throws;
-        else if( tag == "!shouldfail" )
-            return TestCaseInfo::ShouldFail;
-        else if( tag == "!mayfail" )
-            return TestCaseInfo::MayFail;
-        else if( tag == "!nonportable" )
-            return TestCaseInfo::NonPortable;
-        else
-            return TestCaseInfo::None;
-    }
-    inline bool isReservedTag( std::string const& tag ) {
-        return parseSpecialTag( tag ) == TestCaseInfo::None && tag.size() > 0 && !std::isalnum( tag[0] );
-    }
-    inline void enforceNotReservedTag( std::string const& tag, SourceLineInfo const& _lineInfo ) {
-        if( isReservedTag( tag ) ) {
-            std::ostringstream ss;
-            ss << Colour(Colour::Red)
-               << "Tag name [" << tag << "] not allowed.\n"
-               << "Tag names starting with non alpha-numeric characters are reserved\n"
-               << Colour(Colour::FileName)
-               << _lineInfo << '\n';
-            throw std::runtime_error(ss.str());
+    namespace {
+        TestCaseInfo::SpecialProperties parseSpecialTag( std::string const& tag ) {
+            if( startsWith( tag, '.' ) ||
+                tag == "!hide" )
+                return TestCaseInfo::IsHidden;
+            else if( tag == "!throws" )
+                return TestCaseInfo::Throws;
+            else if( tag == "!shouldfail" )
+                return TestCaseInfo::ShouldFail;
+            else if( tag == "!mayfail" )
+                return TestCaseInfo::MayFail;
+            else if( tag == "!nonportable" )
+                return TestCaseInfo::NonPortable;
+            else if( tag == "!benchmark" )
+                return static_cast<TestCaseInfo::SpecialProperties>( TestCaseInfo::Benchmark | TestCaseInfo::IsHidden );
+            else
+                return TestCaseInfo::None;
+        }
+        bool isReservedTag( std::string const& tag ) {
+            return parseSpecialTag( tag ) == TestCaseInfo::None && tag.size() > 0 && !std::isalnum( static_cast<unsigned char>(tag[0]) );
+        }
+        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 alphanumeric characters are reserved\n"
+                          << _lineInfo );
         }
     }
 
-    TestCase makeTestCase(  ITestCase* _testCase,
+    TestCase makeTestCase(  ITestInvoker* _testCase,
                             std::string const& _className,
-                            std::string const& _name,
-                            std::string const& _descOrTags,
+                            NameAndTags const& nameAndTags,
                             SourceLineInfo const& _lineInfo )
     {
-        bool isHidden( startsWith( _name, "./" ) ); // Legacy support
+        bool isHidden = false;
 
         // Parse out tags
-        std::set<std::string> tags;
+        std::vector<std::string> tags;
         std::string desc, tag;
         bool inTag = false;
-        for( std::size_t i = 0; i < _descOrTags.size(); ++i ) {
-            char c = _descOrTags[i];
+        for (char c : nameAndTags.tags) {
             if( !inTag ) {
                 if( c == '[' )
                     inTag = true;
@@ -8310,12 +14072,18 @@ namespace Catch {
             else {
                 if( c == ']' ) {
                     TestCaseInfo::SpecialProperties prop = parseSpecialTag( tag );
-                    if( prop == TestCaseInfo::IsHidden )
+                    if( ( prop & TestCaseInfo::IsHidden ) != 0 )
                         isHidden = true;
                     else if( prop == TestCaseInfo::None )
                         enforceNotReservedTag( tag, _lineInfo );
 
-                    tags.insert( tag );
+                    // 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;
                 }
@@ -8324,33 +14092,31 @@ namespace Catch {
             }
         }
         if( isHidden ) {
-            tags.insert( "hide" );
-            tags.insert( "." );
+            // Add all "hidden" tags to make them behave identically
+            tags.insert( tags.end(), { ".", "!hide" } );
         }
 
-        TestCaseInfo info( _name, _className, desc, tags, _lineInfo );
-        return TestCase( _testCase, info );
+        TestCaseInfo info( static_cast<std::string>(nameAndTags.name), _className, desc, tags, _lineInfo );
+        return TestCase( _testCase, std::move(info) );
     }
 
-    void setTags( TestCaseInfo& testCaseInfo, std::set<std::string> const& tags )
-    {
-        testCaseInfo.tags = tags;
+    void setTags( TestCaseInfo& testCaseInfo, std::vector<std::string> tags ) {
+        std::sort(begin(tags), end(tags));
+        tags.erase(std::unique(begin(tags), end(tags)), end(tags));
         testCaseInfo.lcaseTags.clear();
 
-        std::ostringstream oss;
-        for( std::set<std::string>::const_iterator it = tags.begin(), itEnd = tags.end(); it != itEnd; ++it ) {
-            oss << '[' << *it << ']';
-            std::string lcaseTag = toLower( *it );
+        for( auto const& tag : tags ) {
+            std::string lcaseTag = toLower( tag );
             testCaseInfo.properties = static_cast<TestCaseInfo::SpecialProperties>( testCaseInfo.properties | parseSpecialTag( lcaseTag ) );
-            testCaseInfo.lcaseTags.insert( lcaseTag );
+            testCaseInfo.lcaseTags.push_back( lcaseTag );
         }
-        testCaseInfo.tagsAsString = oss.str();
+        testCaseInfo.tags = std::move(tags);
     }
 
     TestCaseInfo::TestCaseInfo( std::string const& _name,
                                 std::string const& _className,
                                 std::string const& _description,
-                                std::set<std::string> const& _tags,
+                                std::vector<std::string> const& _tags,
                                 SourceLineInfo const& _lineInfo )
     :   name( _name ),
         className( _className ),
@@ -8361,17 +14127,6 @@ namespace Catch {
         setTags( *this, _tags );
     }
 
-    TestCaseInfo::TestCaseInfo( TestCaseInfo const& other )
-    :   name( other.name ),
-        className( other.className ),
-        description( other.description ),
-        tags( other.tags ),
-        lcaseTags( other.lcaseTags ),
-        tagsAsString( other.tagsAsString ),
-        lineInfo( other.lineInfo ),
-        properties( other.properties )
-    {}
-
     bool TestCaseInfo::isHidden() const {
         return ( properties & IsHidden ) != 0;
     }
@@ -8385,12 +14140,24 @@ namespace Catch {
         return ( properties & (ShouldFail ) ) != 0;
     }
 
-    TestCase::TestCase( ITestCase* testCase, TestCaseInfo const& info ) : TestCaseInfo( info ), test( testCase ) {}
+    std::string TestCaseInfo::tagsAsString() const {
+        std::string ret;
+        // '[' and ']' per tag
+        std::size_t full_size = 2 * tags.size();
+        for (const auto& tag : tags) {
+            full_size += tag.size();
+        }
+        ret.reserve(full_size);
+        for (const auto& tag : tags) {
+            ret.push_back('[');
+            ret.append(tag);
+            ret.push_back(']');
+        }
 
-    TestCase::TestCase( TestCase const& other )
-    :   TestCaseInfo( other ),
-        test( other.test )
-    {}
+        return ret;
+    }
+
+    TestCase::TestCase( ITestInvoker* testCase, TestCaseInfo&& info ) : TestCaseInfo( std::move(info) ), test( testCase ) {}
 
     TestCase TestCase::withName( std::string const& _newName ) const {
         TestCase other( *this );
@@ -8398,18 +14165,6 @@ namespace Catch {
         return other;
     }
 
-    void TestCase::swap( TestCase& other ) {
-        test.swap( other.test );
-        name.swap( other.name );
-        className.swap( other.className );
-        description.swap( other.description );
-        tags.swap( other.tags );
-        lcaseTags.swap( other.lcaseTags );
-        tagsAsString.swap( other.tagsAsString );
-        std::swap( TestCaseInfo::properties, static_cast<TestCaseInfo&>( other ).properties );
-        std::swap( lineInfo, other.lineInfo );
-    }
-
     void TestCase::invoke() const {
         test->invoke();
     }
@@ -8423,11 +14178,6 @@ namespace Catch {
     bool TestCase::operator < ( TestCase const& other ) const {
         return name < other.name;
     }
-    TestCase& TestCase::operator = ( TestCase const& other ) {
-        TestCase temp( other );
-        swap( temp );
-        return *this;
-    }
 
     TestCaseInfo const& TestCase::getTestCaseInfo() const
     {
@@ -8435,1995 +14185,2624 @@ namespace Catch {
     }
 
 } // end namespace Catch
+// end catch_test_case_info.cpp
+// start catch_test_case_registry_impl.cpp
 
-// #included from: catch_version.hpp
-#define TWOBLUECUBES_CATCH_VERSION_HPP_INCLUDED
-
-namespace Catch {
-
-    Version::Version
-        (   unsigned int _majorVersion,
-            unsigned int _minorVersion,
-            unsigned int _patchNumber,
-            char const * const _branchName,
-            unsigned int _buildNumber )
-    :   majorVersion( _majorVersion ),
-        minorVersion( _minorVersion ),
-        patchNumber( _patchNumber ),
-        branchName( _branchName ),
-        buildNumber( _buildNumber )
-    {}
-
-    std::ostream& operator << ( std::ostream& os, Version const& version ) {
-        os  << version.majorVersion << '.'
-            << version.minorVersion << '.'
-            << version.patchNumber;
-        // branchName is never null -> 0th char is \0 if it is empty
-        if (version.branchName[0]) {
-            os << '-' << version.branchName
-               << '.' << version.buildNumber;
-        }
-        return os;
-    }
-
-    inline Version libraryVersion() {
-        static Version version( 1, 12, 2, "", 0 );
-        return version;
-    }
-
-}
-
-// #included from: catch_message.hpp
-#define TWOBLUECUBES_CATCH_MESSAGE_HPP_INCLUDED
+#include <algorithm>
+#include <sstream>
 
 namespace Catch {
 
-    MessageInfo::MessageInfo(   std::string const& _macroName,
-                                SourceLineInfo const& _lineInfo,
-                                ResultWas::OfType _type )
-    :   macroName( _macroName ),
-        lineInfo( _lineInfo ),
-        type( _type ),
-        sequence( ++globalCount )
-    {}
-
-    // This may need protecting if threading support is added
-    unsigned int MessageInfo::globalCount = 0;
-
-    ////////////////////////////////////////////////////////////////////////////
-
-    ScopedMessage::ScopedMessage( MessageBuilder const& builder )
-    : m_info( builder.m_info )
-    {
-        m_info.message = builder.m_stream.str();
-        getResultCapture().pushScopedMessage( m_info );
-    }
-    ScopedMessage::ScopedMessage( ScopedMessage const& other )
-    : m_info( other.m_info )
-    {}
-
-#if defined(_MSC_VER)
-#pragma warning(push)
-#pragma warning(disable:4996) // std::uncaught_exception is deprecated in C++17
-#endif
-    ScopedMessage::~ScopedMessage() {
-        if ( !std::uncaught_exception() ){
-            getResultCapture().popScopedMessage(m_info);
-        }
-    }
-#if defined(_MSC_VER)
-#pragma warning(pop)
-#endif
-
-} // end namespace Catch
-
-// #included from: catch_legacy_reporter_adapter.hpp
-#define TWOBLUECUBES_CATCH_LEGACY_REPORTER_ADAPTER_HPP_INCLUDED
-
-// #included from: catch_legacy_reporter_adapter.h
-#define TWOBLUECUBES_CATCH_LEGACY_REPORTER_ADAPTER_H_INCLUDED
-
-namespace Catch
-{
-    // Deprecated
-    struct IReporter : IShared {
-        virtual ~IReporter();
-
-        virtual bool shouldRedirectStdout() const = 0;
-
-        virtual void StartTesting() = 0;
-        virtual void EndTesting( Totals const& totals ) = 0;
-        virtual void StartGroup( std::string const& groupName ) = 0;
-        virtual void EndGroup( std::string const& groupName, Totals const& totals ) = 0;
-        virtual void StartTestCase( TestCaseInfo const& testInfo ) = 0;
-        virtual void EndTestCase( TestCaseInfo const& testInfo, Totals const& totals, std::string const& stdOut, std::string const& stdErr ) = 0;
-        virtual void StartSection( std::string const& sectionName, std::string const& description ) = 0;
-        virtual void EndSection( std::string const& sectionName, Counts const& assertions ) = 0;
-        virtual void NoAssertionsInSection( std::string const& sectionName ) = 0;
-        virtual void NoAssertionsInTestCase( std::string const& testName ) = 0;
-        virtual void Aborted() = 0;
-        virtual void Result( AssertionResult const& result ) = 0;
-    };
-
-    class LegacyReporterAdapter : public SharedImpl<IStreamingReporter>
-    {
-    public:
-        LegacyReporterAdapter( Ptr<IReporter> const& legacyReporter );
-        virtual ~LegacyReporterAdapter();
-
-        virtual ReporterPreferences getPreferences() const;
-        virtual void noMatchingTestCases( std::string const& );
-        virtual void testRunStarting( TestRunInfo const& );
-        virtual void testGroupStarting( GroupInfo const& groupInfo );
-        virtual void testCaseStarting( TestCaseInfo const& testInfo );
-        virtual void sectionStarting( SectionInfo const& sectionInfo );
-        virtual void assertionStarting( AssertionInfo const& );
-        virtual bool assertionEnded( AssertionStats const& assertionStats );
-        virtual void sectionEnded( SectionStats const& sectionStats );
-        virtual void testCaseEnded( TestCaseStats const& testCaseStats );
-        virtual void testGroupEnded( TestGroupStats const& testGroupStats );
-        virtual void testRunEnded( TestRunStats const& testRunStats );
-        virtual void skipTest( TestCaseInfo const& );
-
-    private:
-        Ptr<IReporter> m_legacyReporter;
-    };
-}
-
-namespace Catch
-{
-    LegacyReporterAdapter::LegacyReporterAdapter( Ptr<IReporter> const& legacyReporter )
-    :   m_legacyReporter( legacyReporter )
-    {}
-    LegacyReporterAdapter::~LegacyReporterAdapter() {}
-
-    ReporterPreferences LegacyReporterAdapter::getPreferences() const {
-        ReporterPreferences prefs;
-        prefs.shouldRedirectStdOut = m_legacyReporter->shouldRedirectStdout();
-        return prefs;
-    }
-
-    void LegacyReporterAdapter::noMatchingTestCases( std::string const& ) {}
-    void LegacyReporterAdapter::testRunStarting( TestRunInfo const& ) {
-        m_legacyReporter->StartTesting();
-    }
-    void LegacyReporterAdapter::testGroupStarting( GroupInfo const& groupInfo ) {
-        m_legacyReporter->StartGroup( groupInfo.name );
-    }
-    void LegacyReporterAdapter::testCaseStarting( TestCaseInfo const& testInfo ) {
-        m_legacyReporter->StartTestCase( testInfo );
-    }
-    void LegacyReporterAdapter::sectionStarting( SectionInfo const& sectionInfo ) {
-        m_legacyReporter->StartSection( sectionInfo.name, sectionInfo.description );
-    }
-    void LegacyReporterAdapter::assertionStarting( AssertionInfo const& ) {
-        // Not on legacy interface
-    }
-
-    bool LegacyReporterAdapter::assertionEnded( AssertionStats const& assertionStats ) {
-        if( assertionStats.assertionResult.getResultType() != ResultWas::Ok ) {
-            for( std::vector<MessageInfo>::const_iterator it = assertionStats.infoMessages.begin(), itEnd = assertionStats.infoMessages.end();
-                    it != itEnd;
-                    ++it ) {
-                if( it->type == ResultWas::Info ) {
-                    ResultBuilder rb( it->macroName.c_str(), it->lineInfo, "", ResultDisposition::Normal );
-                    rb << it->message;
-                    rb.setResultType( ResultWas::Info );
-                    AssertionResult result = rb.build();
-                    m_legacyReporter->Result( result );
+    namespace {
+        struct TestHasher {
+            using hash_t = uint64_t;
+
+            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;
             }
-        }
-        m_legacyReporter->Result( assertionStats.assertionResult );
-        return true;
-    }
-    void LegacyReporterAdapter::sectionEnded( SectionStats const& sectionStats ) {
-        if( sectionStats.missingAssertions )
-            m_legacyReporter->NoAssertionsInSection( sectionStats.sectionInfo.name );
-        m_legacyReporter->EndSection( sectionStats.sectionInfo.name, sectionStats.assertions );
-    }
-    void LegacyReporterAdapter::testCaseEnded( TestCaseStats const& testCaseStats ) {
-        m_legacyReporter->EndTestCase
-            (   testCaseStats.testInfo,
-                testCaseStats.totals,
-                testCaseStats.stdOut,
-                testCaseStats.stdErr );
-    }
-    void LegacyReporterAdapter::testGroupEnded( TestGroupStats const& testGroupStats ) {
-        if( testGroupStats.aborting )
-            m_legacyReporter->Aborted();
-        m_legacyReporter->EndGroup( testGroupStats.groupInfo.name, testGroupStats.totals );
-    }
-    void LegacyReporterAdapter::testRunEnded( TestRunStats const& testRunStats ) {
-        m_legacyReporter->EndTesting( testRunStats.totals );
-    }
-    void LegacyReporterAdapter::skipTest( TestCaseInfo const& ) {
-    }
-}
-
-// #included from: catch_timer.hpp
-
-#ifdef __clang__
-#pragma clang diagnostic push
-#pragma clang diagnostic ignored "-Wc++11-long-long"
-#endif
-
-#ifdef CATCH_PLATFORM_WINDOWS
-
-#else
 
-#include <sys/time.h>
-
-#endif
+        private:
+            hash_t m_hashSuffix;
+        };
+    } // end unnamed namespace
 
-namespace Catch {
+    std::vector<TestCase> sortTests( IConfig const& config, std::vector<TestCase> const& unsortedTestCases ) {
+        switch( config.runOrder() ) {
+            case RunTests::InDeclarationOrder:
+                // already in declaration order
+                break;
 
-    namespace {
-#ifdef CATCH_PLATFORM_WINDOWS
-        UInt64 getCurrentTicks() {
-            static UInt64 hz=0, hzo=0;
-            if (!hz) {
-                QueryPerformanceFrequency( reinterpret_cast<LARGE_INTEGER*>( &hz ) );
-                QueryPerformanceCounter( reinterpret_cast<LARGE_INTEGER*>( &hzo ) );
+            case RunTests::InLexicographicalOrder: {
+                std::vector<TestCase> sorted = unsortedTestCases;
+                std::sort( sorted.begin(), sorted.end() );
+                return sorted;
             }
-            UInt64 t;
-            QueryPerformanceCounter( reinterpret_cast<LARGE_INTEGER*>( &t ) );
-            return ((t-hzo)*1000000)/hz;
-        }
-#else
-        UInt64 getCurrentTicks() {
-            timeval t;
-            gettimeofday(&t,CATCH_NULL);
-            return static_cast<UInt64>( t.tv_sec ) * 1000000ull + static_cast<UInt64>( t.tv_usec );
-        }
-#endif
-    }
-
-    void Timer::start() {
-        m_ticks = getCurrentTicks();
-    }
-    unsigned int Timer::getElapsedMicroseconds() const {
-        return static_cast<unsigned int>(getCurrentTicks() - m_ticks);
-    }
-    unsigned int Timer::getElapsedMilliseconds() const {
-        return static_cast<unsigned int>(getElapsedMicroseconds()/1000);
-    }
-    double Timer::getElapsedSeconds() const {
-        return getElapsedMicroseconds()/1000000.0;
-    }
-
-} // namespace Catch
-
-#ifdef __clang__
-#pragma clang diagnostic pop
-#endif
-// #included from: catch_common.hpp
-#define TWOBLUECUBES_CATCH_COMMON_HPP_INCLUDED
-
-#include <cstring>
-#include <cctype>
-
-namespace Catch {
 
-    bool startsWith( std::string const& s, std::string const& prefix ) {
-        return s.size() >= prefix.size() && std::equal(prefix.begin(), prefix.end(), s.begin());
-    }
-    bool startsWith( std::string const& s, char prefix ) {
-        return !s.empty() && s[0] == prefix;
-    }
-    bool endsWith( std::string const& s, std::string const& suffix ) {
-        return s.size() >= suffix.size() && std::equal(suffix.rbegin(), suffix.rend(), s.rbegin());
-    }
-    bool endsWith( std::string const& s, char suffix ) {
-        return !s.empty() && s[s.size()-1] == suffix;
-    }
-    bool contains( std::string const& s, std::string const& infix ) {
-        return s.find( infix ) != std::string::npos;
-    }
-    char toLowerCh(char c) {
-        return static_cast<char>( std::tolower( c ) );
-    }
-    void toLowerInPlace( std::string& s ) {
-        std::transform( s.begin(), s.end(), s.begin(), toLowerCh );
-    }
-    std::string toLower( std::string const& s ) {
-        std::string lc = s;
-        toLowerInPlace( lc );
-        return lc;
-    }
-    std::string trim( std::string const& str ) {
-        static char const* whitespaceChars = "\n\r\t ";
-        std::string::size_type start = str.find_first_not_of( whitespaceChars );
-        std::string::size_type end = str.find_last_not_of( whitespaceChars );
+            case RunTests::InRandomOrder: {
+                seedRng( config );
+                TestHasher h{ config.rngSeed() };
 
-        return start != std::string::npos ? str.substr( start, 1+end-start ) : std::string();
-    }
+                using hashedTest = std::pair<TestHasher::hash_t, TestCase const*>;
+                std::vector<hashedTest> indexed_tests;
+                indexed_tests.reserve( unsortedTestCases.size() );
 
-    bool replaceInPlace( std::string& str, std::string const& replaceThis, std::string const& withThis ) {
-        bool replaced = false;
-        std::size_t i = str.find( replaceThis );
-        while( i != std::string::npos ) {
-            replaced = true;
-            str = str.substr( 0, i ) + withThis + str.substr( i+replaceThis.size() );
-            if( i < str.size()-withThis.size() )
-                i = str.find( replaceThis, i+withThis.size() );
-            else
-                i = std::string::npos;
-        }
-        return replaced;
-    }
+                for (auto const& testCase : unsortedTestCases) {
+                    indexed_tests.emplace_back(h(testCase), &testCase);
+                }
 
-    pluralise::pluralise( std::size_t count, std::string const& label )
-    :   m_count( count ),
-        m_label( label )
-    {}
+                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::ostream& operator << ( std::ostream& os, pluralise const& pluraliser ) {
-        os << pluraliser.m_count << ' ' << pluraliser.m_label;
-        if( pluraliser.m_count != 1 )
-            os << 's';
-        return os;
+                std::vector<TestCase> sorted;
+                sorted.reserve( indexed_tests.size() );
+
+                for (auto const& hashed : indexed_tests) {
+                    sorted.emplace_back(*hashed.second);
+                }
+
+                return sorted;
+            }
+        }
+        return unsortedTestCases;
     }
 
-    SourceLineInfo::SourceLineInfo() : file(""), line( 0 ){}
-    SourceLineInfo::SourceLineInfo( char const* _file, std::size_t _line )
-    :   file( _file ),
-        line( _line )
-    {}
-    bool SourceLineInfo::empty() const {
-        return file[0] == '\0';
+    bool isThrowSafe( TestCase const& testCase, IConfig const& config ) {
+        return !testCase.throws() || config.allowThrows();
     }
-    bool SourceLineInfo::operator == ( SourceLineInfo const& other ) const {
-        return line == other.line && (file == other.file || std::strcmp(file, other.file) == 0);
+
+    bool matchTest( TestCase const& testCase, TestSpec const& testSpec, IConfig const& config ) {
+        return testSpec.matches( testCase ) && isThrowSafe( testCase, config );
     }
-    bool SourceLineInfo::operator < ( SourceLineInfo const& other ) const {
-        return line < other.line || ( line == other.line && (std::strcmp(file, other.file) < 0));
+
+    void enforceNoDuplicateTestCases( std::vector<TestCase> const& functions ) {
+        std::set<TestCase> seenFunctions;
+        for( auto const& function : functions ) {
+            auto prev = seenFunctions.insert( function );
+            CATCH_ENFORCE( prev.second,
+                    "error: TEST_CASE( \"" << function.name << "\" ) already defined.\n"
+                    << "\tFirst seen at " << prev.first->getTestCaseInfo().lineInfo << "\n"
+                    << "\tRedefined at " << function.getTestCaseInfo().lineInfo );
+        }
     }
 
-    void seedRng( IConfig const& config ) {
-        if( config.rngSeed() != 0 )
-            std::srand( config.rngSeed() );
+    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 ((!testSpec.hasFilters() && !testCase.isHidden()) ||
+                (testSpec.hasFilters() && matchTest(testCase, testSpec, config))) {
+                filtered.push_back(testCase);
+            }
+        }
+        return filtered;
     }
-    unsigned int rngSeed() {
-        return getCurrentContext().getConfig()->rngSeed();
+    std::vector<TestCase> const& getAllTestCasesSorted( IConfig const& config ) {
+        return getRegistryHub().getTestCaseRegistry().getAllTestsSorted( config );
     }
 
-    std::ostream& operator << ( std::ostream& os, SourceLineInfo const& info ) {
-#ifndef __GNUG__
-        os << info.file << '(' << info.line << ')';
-#else
-        os << info.file << ':' << info.line;
-#endif
-        return os;
+    void TestRegistry::registerTest( TestCase const& testCase ) {
+        std::string name = testCase.getTestCaseInfo().name;
+        if( name.empty() ) {
+            ReusableStringStream rss;
+            rss << "Anonymous test case " << ++m_unnamedCount;
+            return registerTest( testCase.withName( rss.str() ) );
+        }
+        m_functions.push_back( testCase );
     }
 
-    void throwLogicError( std::string const& message, SourceLineInfo const& locationInfo ) {
-        std::ostringstream oss;
-        oss << locationInfo << ": Internal Catch error: '" << message << '\'';
-        if( alwaysTrue() )
-            throw std::logic_error( oss.str() );
+    std::vector<TestCase> const& TestRegistry::getAllTests() const {
+        return m_functions;
     }
-}
-
-// #included from: catch_section.hpp
-#define TWOBLUECUBES_CATCH_SECTION_HPP_INCLUDED
+    std::vector<TestCase> const& TestRegistry::getAllTestsSorted( IConfig const& config ) const {
+        if( m_sortedFunctions.empty() )
+            enforceNoDuplicateTestCases( m_functions );
 
-namespace Catch {
+        if(  m_currentSortOrder != config.runOrder() || m_sortedFunctions.empty() ) {
+            m_sortedFunctions = sortTests( config, m_functions );
+            m_currentSortOrder = config.runOrder();
+        }
+        return m_sortedFunctions;
+    }
 
-    SectionInfo::SectionInfo
-        (   SourceLineInfo const& _lineInfo,
-            std::string const& _name,
-            std::string const& _description )
-    :   name( _name ),
-        description( _description ),
-        lineInfo( _lineInfo )
-    {}
+    ///////////////////////////////////////////////////////////////////////////
+    TestInvokerAsFunction::TestInvokerAsFunction( void(*testAsFunction)() ) noexcept : m_testAsFunction( testAsFunction ) {}
 
-    Section::Section( SectionInfo const& info )
-    :   m_info( info ),
-        m_sectionIncluded( getResultCapture().sectionStarted( m_info, m_assertions ) )
-    {
-        m_timer.start();
+    void TestInvokerAsFunction::invoke() const {
+        m_testAsFunction();
     }
 
-#if defined(_MSC_VER)
-#pragma warning(push)
-#pragma warning(disable:4996) // std::uncaught_exception is deprecated in C++17
-#endif
-    Section::~Section() {
-        if( m_sectionIncluded ) {
-            SectionEndInfo endInfo( m_info, m_assertions, m_timer.getElapsedSeconds() );
-            if( std::uncaught_exception() )
-                getResultCapture().sectionEndedEarly( endInfo );
-            else
-                getResultCapture().sectionEnded( endInfo );
+    std::string extractClassName( StringRef const& classOrQualifiedMethodName ) {
+        std::string className(classOrQualifiedMethodName);
+        if( startsWith( className, '&' ) )
+        {
+            std::size_t lastColons = className.rfind( "::" );
+            std::size_t penultimateColons = className.rfind( "::", lastColons-1 );
+            if( penultimateColons == std::string::npos )
+                penultimateColons = 1;
+            className = className.substr( penultimateColons, lastColons-penultimateColons );
         }
+        return className;
     }
-#if defined(_MSC_VER)
-#pragma warning(pop)
+
+} // end namespace Catch
+// end catch_test_case_registry_impl.cpp
+// start catch_test_case_tracker.cpp
+
+#include <algorithm>
+#include <cassert>
+#include <stdexcept>
+#include <memory>
+#include <sstream>
+
+#if defined(__clang__)
+#    pragma clang diagnostic push
+#    pragma clang diagnostic ignored "-Wexit-time-destructors"
 #endif
 
-    // This indicates whether the section should be executed or not
-    Section::operator bool() const {
-        return m_sectionIncluded;
-    }
+namespace Catch {
+namespace TestCaseTracking {
 
-} // end namespace Catch
+    NameAndLocation::NameAndLocation( std::string const& _name, SourceLineInfo const& _location )
+    :   name( _name ),
+        location( _location )
+    {}
 
-// #included from: catch_debugger.hpp
-#define TWOBLUECUBES_CATCH_DEBUGGER_HPP_INCLUDED
+    ITracker::~ITracker() = default;
 
-#ifdef CATCH_PLATFORM_MAC
+    ITracker& TrackerContext::startRun() {
+        m_rootTracker = std::make_shared<SectionTracker>( NameAndLocation( "{root}", CATCH_INTERNAL_LINEINFO ), *this, nullptr );
+        m_currentTracker = nullptr;
+        m_runState = Executing;
+        return *m_rootTracker;
+    }
 
-    #include <assert.h>
-    #include <stdbool.h>
-    #include <sys/types.h>
-    #include <unistd.h>
-    #include <sys/sysctl.h>
+    void TrackerContext::endRun() {
+        m_rootTracker.reset();
+        m_currentTracker = nullptr;
+        m_runState = NotStarted;
+    }
 
-    namespace Catch{
+    void TrackerContext::startCycle() {
+        m_currentTracker = m_rootTracker.get();
+        m_runState = Executing;
+    }
+    void TrackerContext::completeCycle() {
+        m_runState = CompletedCycle;
+    }
 
-        // The following function is taken directly from the following technical note:
-        // http://developer.apple.com/library/mac/#qa/qa2004/qa1361.html
+    bool TrackerContext::completedCycle() const {
+        return m_runState == CompletedCycle;
+    }
+    ITracker& TrackerContext::currentTracker() {
+        return *m_currentTracker;
+    }
+    void TrackerContext::setCurrentTracker( ITracker* tracker ) {
+        m_currentTracker = tracker;
+    }
 
-        // Returns true if the current process is being debugged (either
-        // running under the debugger or has a debugger attached post facto).
-        bool isDebuggerActive(){
+    TrackerBase::TrackerBase( NameAndLocation const& nameAndLocation, TrackerContext& ctx, ITracker* parent ):
+        ITracker(nameAndLocation),
+        m_ctx( ctx ),
+        m_parent( parent )
+    {}
 
-            int                 mib[4];
-            struct kinfo_proc   info;
-            size_t              size;
+    bool TrackerBase::isComplete() const {
+        return m_runState == CompletedSuccessfully || m_runState == Failed;
+    }
+    bool TrackerBase::isSuccessfullyCompleted() const {
+        return m_runState == CompletedSuccessfully;
+    }
+    bool TrackerBase::isOpen() const {
+        return m_runState != NotStarted && !isComplete();
+    }
+    bool TrackerBase::hasChildren() const {
+        return !m_children.empty();
+    }
 
-            // Initialize the flags so that, if sysctl fails for some bizarre
-            // reason, we get a predictable result.
+    void TrackerBase::addChild( ITrackerPtr const& child ) {
+        m_children.push_back( child );
+    }
 
-            info.kp_proc.p_flag = 0;
+    ITrackerPtr TrackerBase::findChild( NameAndLocation const& nameAndLocation ) {
+        auto it = std::find_if( m_children.begin(), m_children.end(),
+            [&nameAndLocation]( ITrackerPtr const& tracker ){
+                return
+                    tracker->nameAndLocation().location == nameAndLocation.location &&
+                    tracker->nameAndLocation().name == nameAndLocation.name;
+            } );
+        return( it != m_children.end() )
+            ? *it
+            : nullptr;
+    }
+    ITracker& TrackerBase::parent() {
+        assert( m_parent ); // Should always be non-null except for root
+        return *m_parent;
+    }
 
-            // Initialize mib, which tells sysctl the info we want, in this case
-            // we're looking for information about a specific process ID.
+    void TrackerBase::openChild() {
+        if( m_runState != ExecutingChildren ) {
+            m_runState = ExecutingChildren;
+            if( m_parent )
+                m_parent->openChild();
+        }
+    }
 
-            mib[0] = CTL_KERN;
-            mib[1] = KERN_PROC;
-            mib[2] = KERN_PROC_PID;
-            mib[3] = getpid();
+    bool TrackerBase::isSectionTracker() const { return false; }
+    bool TrackerBase::isGeneratorTracker() const { return false; }
 
-            // Call sysctl.
+    void TrackerBase::open() {
+        m_runState = Executing;
+        moveToThis();
+        if( m_parent )
+            m_parent->openChild();
+    }
 
-            size = sizeof(info);
-            if( sysctl(mib, sizeof(mib) / sizeof(*mib), &info, &size, CATCH_NULL, 0) != 0 ) {
-                Catch::cerr() << "\n** Call to sysctl failed - unable to determine if debugger is active **\n" << std::endl;
-                return false;
-            }
+    void TrackerBase::close() {
 
-            // We're being debugged if the P_TRACED flag is set.
+        // Close any still open children (e.g. generators)
+        while( &m_ctx.currentTracker() != this )
+            m_ctx.currentTracker().close();
 
-            return ( (info.kp_proc.p_flag & P_TRACED) != 0 );
-        }
-    } // namespace Catch
+        switch( m_runState ) {
+            case NeedsAnotherRun:
+                break;
 
-#elif defined(CATCH_PLATFORM_LINUX)
-    #include <fstream>
-    #include <string>
+            case Executing:
+                m_runState = CompletedSuccessfully;
+                break;
+            case ExecutingChildren:
+                if( std::all_of(m_children.begin(), m_children.end(), [](ITrackerPtr const& t){ return t->isComplete(); }) )
+                    m_runState = CompletedSuccessfully;
+                break;
 
-    namespace Catch{
-        // The standard POSIX way of detecting a debugger is to attempt to
-        // ptrace() the process, but this needs to be done from a child and not
-        // this process itself to still allow attaching to this process later
-        // if wanted, so is rather heavy. Under Linux we have the PID of the
-        // "debugger" (which doesn't need to be gdb, of course, it could also
-        // be strace, for example) in /proc/$PID/status, so just get it from
-        // there instead.
-        bool isDebuggerActive(){
-            // Libstdc++ has a bug, where std::ifstream sets errno to 0
-            // This way our users can properly assert over errno values
-            ErrnoGuard guard;
-            std::ifstream in("/proc/self/status");
-            for( std::string line; std::getline(in, line); ) {
-                static const int PREFIX_LEN = 11;
-                if( line.compare(0, PREFIX_LEN, "TracerPid:\t") == 0 ) {
-                    // We're traced if the PID is not 0 and no other PID starts
-                    // with 0 digit, so it's enough to check for just a single
-                    // character.
-                    return line.length() > PREFIX_LEN && line[PREFIX_LEN] != '0';
-                }
-            }
+            case NotStarted:
+            case CompletedSuccessfully:
+            case Failed:
+                CATCH_INTERNAL_ERROR( "Illogical state: " << m_runState );
 
-            return false;
-        }
-    } // namespace Catch
-#elif defined(_MSC_VER)
-    extern "C" __declspec(dllimport) int __stdcall IsDebuggerPresent();
-    namespace Catch {
-        bool isDebuggerActive() {
-            return IsDebuggerPresent() != 0;
+            default:
+                CATCH_INTERNAL_ERROR( "Unknown state: " << m_runState );
         }
+        moveToParent();
+        m_ctx.completeCycle();
     }
-#elif defined(__MINGW32__)
-    extern "C" __declspec(dllimport) int __stdcall IsDebuggerPresent();
-    namespace Catch {
-        bool isDebuggerActive() {
-            return IsDebuggerPresent() != 0;
-        }
+    void TrackerBase::fail() {
+        m_runState = Failed;
+        if( m_parent )
+            m_parent->markAsNeedingAnotherRun();
+        moveToParent();
+        m_ctx.completeCycle();
     }
-#else
-    namespace Catch {
-       inline bool isDebuggerActive() { return false; }
+    void TrackerBase::markAsNeedingAnotherRun() {
+        m_runState = NeedsAnotherRun;
     }
-#endif // Platform
-
-#ifdef CATCH_PLATFORM_WINDOWS
 
-    namespace Catch {
-        void writeToDebugConsole( std::string const& text ) {
-            ::OutputDebugStringA( text.c_str() );
-        }
+    void TrackerBase::moveToParent() {
+        assert( m_parent );
+        m_ctx.setCurrentTracker( m_parent );
     }
-#else
-    namespace Catch {
-        void writeToDebugConsole( std::string const& text ) {
-            // !TBD: Need a version for Mac/ XCode and other IDEs
-            Catch::cout() << text;
-        }
+    void TrackerBase::moveToThis() {
+        m_ctx.setCurrentTracker( this );
     }
-#endif // Platform
 
-// #included from: catch_tostring.hpp
-#define TWOBLUECUBES_CATCH_TOSTRING_HPP_INCLUDED
+    SectionTracker::SectionTracker( NameAndLocation const& nameAndLocation, TrackerContext& ctx, ITracker* parent )
+    :   TrackerBase( nameAndLocation, ctx, parent ),
+        m_trimmed_name(trim(nameAndLocation.name))
+    {
+        if( parent ) {
+            while( !parent->isSectionTracker() )
+                parent = &parent->parent();
 
-namespace Catch {
+            SectionTracker& parentSection = static_cast<SectionTracker&>( *parent );
+            addNextFilters( parentSection.m_filters );
+        }
+    }
 
-namespace Detail {
+    bool SectionTracker::isComplete() const {
+        bool complete = true;
 
-    const std::string unprintableString = "{?}";
+        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;
+    }
 
-    namespace {
-        const int hexThreshold = 255;
+    bool SectionTracker::isSectionTracker() const { return true; }
 
-        struct Endianness {
-            enum Arch { Big, Little };
+    SectionTracker& SectionTracker::acquire( TrackerContext& ctx, NameAndLocation const& nameAndLocation ) {
+        std::shared_ptr<SectionTracker> section;
 
-            static Arch which() {
-                union _{
-                    int asInt;
-                    char asChar[sizeof (int)];
-                } u;
+        ITracker& currentTracker = ctx.currentTracker();
+        if( ITrackerPtr childTracker = currentTracker.findChild( nameAndLocation ) ) {
+            assert( childTracker );
+            assert( childTracker->isSectionTracker() );
+            section = std::static_pointer_cast<SectionTracker>( childTracker );
+        }
+        else {
+            section = std::make_shared<SectionTracker>( nameAndLocation, ctx, &currentTracker );
+            currentTracker.addChild( section );
+        }
+        if( !ctx.completedCycle() )
+            section->tryOpen();
+        return *section;
+    }
 
-                u.asInt = 1;
-                return ( u.asChar[sizeof(int)-1] == 1 ) ? Big : Little;
-            }
-        };
+    void SectionTracker::tryOpen() {
+        if( !isComplete() )
+            open();
     }
 
-    std::string rawMemoryToString( const void *object, std::size_t size )
-    {
-        // Reverse order for little endian architectures
-        int i = 0, end = static_cast<int>( size ), inc = 1;
-        if( Endianness::which() == Endianness::Little ) {
-            i = end-1;
-            end = inc = -1;
+    void SectionTracker::addInitialFilters( std::vector<std::string> const& filters ) {
+        if( !filters.empty() ) {
+            m_filters.reserve( m_filters.size() + filters.size() + 2 );
+            m_filters.emplace_back(""); // Root - should never be consulted
+            m_filters.emplace_back(""); // Test Case - not a section filter
+            m_filters.insert( m_filters.end(), filters.begin(), filters.end() );
         }
-
-        unsigned char const *bytes = static_cast<unsigned char const *>(object);
-        std::ostringstream os;
-        os << "0x" << std::setfill('0') << std::hex;
-        for( ; i != end; i += inc )
-             os << std::setw(2) << static_cast<unsigned>(bytes[i]);
-       return os.str();
     }
-}
+    void SectionTracker::addNextFilters( std::vector<std::string> const& filters ) {
+        if( filters.size() > 1 )
+            m_filters.insert( m_filters.end(), filters.begin()+1, filters.end() );
+    }
 
-std::string toString( std::string const& value ) {
-    std::string s = value;
-    if( getCurrentContext().getConfig()->showInvisibles() ) {
-        for(size_t i = 0; i < s.size(); ++i ) {
-            std::string subs;
-            switch( s[i] ) {
-            case '\n': subs = "\\n"; break;
-            case '\t': subs = "\\t"; break;
-            default: break;
-            }
-            if( !subs.empty() ) {
-                s = s.substr( 0, i ) + subs + s.substr( i+1 );
-                ++i;
-            }
-        }
+    std::vector<std::string> const& SectionTracker::getFilters() const {
+        return m_filters;
     }
-    return '"' + s + '"';
-}
-std::string toString( std::wstring const& value ) {
 
-    std::string s;
-    s.reserve( value.size() );
-    for(size_t i = 0; i < value.size(); ++i )
-        s += value[i] <= 0xff ? static_cast<char>( value[i] ) : '?';
-    return Catch::toString( s );
-}
+    std::string const& SectionTracker::trimmedName() const {
+        return m_trimmed_name;
+    }
 
-std::string toString( const char* const value ) {
-    return value ? Catch::toString( std::string( value ) ) : std::string( "{null string}" );
-}
+} // namespace TestCaseTracking
 
-std::string toString( char* const value ) {
-    return Catch::toString( static_cast<const char*>( value ) );
-}
+using TestCaseTracking::ITracker;
+using TestCaseTracking::TrackerContext;
+using TestCaseTracking::SectionTracker;
 
-std::string toString( const wchar_t* const value )
-{
-    return value ? Catch::toString( std::wstring(value) ) : std::string( "{null string}" );
-}
+} // namespace Catch
 
-std::string toString( wchar_t* const value )
-{
-    return Catch::toString( static_cast<const wchar_t*>( value ) );
-}
+#if defined(__clang__)
+#    pragma clang diagnostic pop
+#endif
+// end catch_test_case_tracker.cpp
+// start catch_test_registry.cpp
 
-std::string toString( int value ) {
-    std::ostringstream oss;
-    oss << value;
-    if( value > Detail::hexThreshold )
-        oss << " (0x" << std::hex << value << ')';
-    return oss.str();
-}
+namespace Catch {
 
-std::string toString( unsigned long value ) {
-    std::ostringstream oss;
-    oss << value;
-    if( value > Detail::hexThreshold )
-        oss << " (0x" << std::hex << value << ')';
-    return oss.str();
-}
+    auto makeTestInvoker( void(*testAsFunction)() ) noexcept -> ITestInvoker* {
+        return new(std::nothrow) TestInvokerAsFunction( testAsFunction );
+    }
 
-std::string toString( unsigned int value ) {
-    return Catch::toString( static_cast<unsigned long>( value ) );
-}
+    NameAndTags::NameAndTags( StringRef const& name_ , StringRef const& tags_ ) noexcept : name( name_ ), tags( tags_ ) {}
 
-template<typename T>
-std::string fpToString( T value, int precision ) {
-    std::ostringstream oss;
-    oss << std::setprecision( precision )
-        << std::fixed
-        << value;
-    std::string d = oss.str();
-    std::size_t i = d.find_last_not_of( '0' );
-    if( i != std::string::npos && i != d.size()-1 ) {
-        if( d[i] == '.' )
-            i++;
-        d = d.substr( 0, i+1 );
+    AutoReg::AutoReg( ITestInvoker* invoker, SourceLineInfo const& lineInfo, StringRef const& classOrMethod, NameAndTags const& nameAndTags ) noexcept {
+        CATCH_TRY {
+            getMutableRegistryHub()
+                    .registerTest(
+                        makeTestCase(
+                            invoker,
+                            extractClassName( classOrMethod ),
+                            nameAndTags,
+                            lineInfo));
+        } CATCH_CATCH_ALL {
+            // Do not throw when constructing global objects, instead register the exception to be processed later
+            getMutableRegistryHub().registerStartupException();
+        }
     }
-    return d;
-}
 
-std::string toString( const double value ) {
-    return fpToString( value, 10 );
-}
-std::string toString( const float value ) {
-    return fpToString( value, 5 ) + 'f';
+    AutoReg::~AutoReg() = default;
 }
+// end catch_test_registry.cpp
+// start catch_test_spec.cpp
 
-std::string toString( bool value ) {
-    return value ? "true" : "false";
-}
+#include <algorithm>
+#include <string>
+#include <vector>
+#include <memory>
 
-std::string toString( char value ) {
-    if ( value == '\r' )
-        return "'\\r'";
-    if ( value == '\f' )
-        return "'\\f'";
-    if ( value == '\n' )
-        return "'\\n'";
-    if ( value == '\t' )
-        return "'\\t'";
-    if ( '\0' <= value && value < ' ' )
-        return toString( static_cast<unsigned int>( value ) );
-    char chstr[] = "' '";
-    chstr[1] = value;
-    return chstr;
-}
+namespace Catch {
 
-std::string toString( signed char value ) {
-    return toString( static_cast<char>( value ) );
-}
+    TestSpec::Pattern::Pattern( std::string const& name )
+    : m_name( name )
+    {}
 
-std::string toString( unsigned char value ) {
-    return toString( static_cast<char>( value ) );
-}
+    TestSpec::Pattern::~Pattern() = default;
 
-#ifdef CATCH_CONFIG_CPP11_LONG_LONG
-std::string toString( long long value ) {
-    std::ostringstream oss;
-    oss << value;
-    if( value > Detail::hexThreshold )
-        oss << " (0x" << std::hex << value << ')';
-    return oss.str();
-}
-std::string toString( unsigned long long value ) {
-    std::ostringstream oss;
-    oss << value;
-    if( value > Detail::hexThreshold )
-        oss << " (0x" << std::hex << value << ')';
-    return oss.str();
-}
-#endif
+    std::string const& TestSpec::Pattern::name() const {
+        return m_name;
+    }
 
-#ifdef CATCH_CONFIG_CPP11_NULLPTR
-std::string toString( std::nullptr_t ) {
-    return "nullptr";
-}
-#endif
+    TestSpec::NamePattern::NamePattern( std::string const& name, std::string const& filterString )
+    : Pattern( filterString )
+    , m_wildcardPattern( toLower( name ), CaseSensitive::No )
+    {}
 
-#ifdef __OBJC__
-    std::string toString( NSString const * const& nsstring ) {
-        if( !nsstring )
-            return "nil";
-        return "@" + toString([nsstring UTF8String]);
+    bool TestSpec::NamePattern::matches( TestCaseInfo const& testCase ) const {
+        return m_wildcardPattern.matches( testCase.name );
     }
-    std::string toString( NSString * CATCH_ARC_STRONG & nsstring ) {
-        if( !nsstring )
-            return "nil";
-        return "@" + toString([nsstring UTF8String]);
+
+    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);
     }
-    std::string toString( NSObject* const& nsObject ) {
-        return toString( [nsObject description] );
+
+    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 );
     }
-#endif
 
-} // end namespace Catch
+    bool TestSpec::Filter::matches( TestCaseInfo const& testCase ) const {
+        return std::all_of( m_patterns.begin(), m_patterns.end(), [&]( PatternPtr const& p ){ return p->matches( testCase ); } );
+    }
 
-// #included from: catch_result_builder.hpp
-#define TWOBLUECUBES_CATCH_RESULT_BUILDER_HPP_INCLUDED
+    std::string TestSpec::Filter::name() const {
+        std::string name;
+        for( auto const& p : m_patterns )
+            name += p->name();
+        return name;
+    }
 
-#include <cassert>
+    bool TestSpec::hasFilters() const {
+        return !m_filters.empty();
+    }
 
-namespace Catch {
+    bool TestSpec::matches( TestCaseInfo const& testCase ) const {
+        return std::any_of( m_filters.begin(), m_filters.end(), [&]( Filter const& f ){ return f.matches( testCase ); } );
+    }
 
-    ResultBuilder::ResultBuilder(   char const* macroName,
-                                    SourceLineInfo const& lineInfo,
-                                    char const* capturedExpression,
-                                    ResultDisposition::Flags resultDisposition,
-                                    char const* secondArg )
-    :   m_assertionInfo( macroName, lineInfo, capturedExpression, resultDisposition, secondArg ),
-        m_shouldDebugBreak( false ),
-        m_shouldThrow( false ),
-        m_guardException( false ),
-        m_usedStream( false )
-    {}
+    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;
+    }
 
-    ResultBuilder::~ResultBuilder() {
-#if defined(CATCH_CONFIG_FAST_COMPILE)
-        if ( m_guardException ) {
-            stream().oss << "Exception translation was disabled by CATCH_CONFIG_FAST_COMPILE";
-            captureResult( ResultWas::ThrewException );
-            getCurrentContext().getResultCapture()->exceptionEarlyReported();
-        }
-#endif
+    const TestSpec::vectorStrings& TestSpec::getInvalidArgs() const{
+        return  (m_invalidArgs);
     }
 
-    ResultBuilder& ResultBuilder::setResultType( ResultWas::OfType result ) {
-        m_data.resultType = result;
+}
+// end catch_test_spec.cpp
+// start catch_test_spec_parser.cpp
+
+namespace Catch {
+
+    TestSpecParser::TestSpecParser( ITagAliasRegistry const& tagAliases ) : m_tagAliases( &tagAliases ) {}
+
+    TestSpecParser& TestSpecParser::parse( std::string const& arg ) {
+        m_mode = None;
+        m_exclusion = false;
+        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 )
+          //if visitChar fails
+           if( !visitChar( m_arg[m_pos] ) ){
+               m_testSpec.m_invalidArgs.push_back(arg);
+               break;
+           }
+        endMode();
         return *this;
     }
-    ResultBuilder& ResultBuilder::setResultType( bool result ) {
-        m_data.resultType = result ? ResultWas::Ok : ResultWas::ExpressionFailed;
-        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 ResultBuilder::endExpression( DecomposedExpression const& expr ) {
-        // Flip bool results if FalseTest flag is set
-        if( isFalseTest( m_assertionInfo.resultDisposition ) ) {
-            m_data.negate( expr.isBinaryExpression() );
+        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;
         }
 
-        getResultCapture().assertionRun();
+        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 );
+        }
+    }
+    bool TestSpecParser::processOtherChar( char c ) {
+        if( !isControlChar( c ) )
+            return false;
+        m_substring += c;
+        endMode();
+        return true;
+    }
+    void TestSpecParser::startNewMode( Mode mode ) {
+        m_mode = mode;
+    }
+    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() {
+        saveLastMode();
+        m_mode = EscapedName;
+        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 == ']';
+        }
+    }
 
-        if(getCurrentContext().getConfig()->includeSuccessfulResults() || m_data.resultType != ResultWas::Ok)
-        {
-            AssertionResult result = build( expr );
-            handleResult( result );
+    void TestSpecParser::addFilter() {
+        if( !m_currentFilter.m_patterns.empty() ) {
+            m_testSpec.m_filters.push_back( m_currentFilter );
+            m_currentFilter = TestSpec::Filter();
         }
-        else
-            getResultCapture().assertionPassed();
     }
 
-    void ResultBuilder::useActiveException( ResultDisposition::Flags resultDisposition ) {
-        m_assertionInfo.resultDisposition = resultDisposition;
-        stream().oss << Catch::translateActiveException();
-        captureResult( ResultWas::ThrewException );
+    void TestSpecParser::saveLastMode() {
+      lastMode = m_mode;
     }
 
-    void ResultBuilder::captureResult( ResultWas::OfType resultType ) {
-        setResultType( resultType );
-        captureExpression();
+    void TestSpecParser::revertBackToLastMode() {
+      m_mode = lastMode;
     }
 
-    void ResultBuilder::captureExpectedException( std::string const& expectedMessage ) {
-        if( expectedMessage.empty() )
-            captureExpectedException( Matchers::Impl::MatchAllOf<std::string>() );
-        else
-            captureExpectedException( Matchers::Equals( expectedMessage ) );
+    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
     }
 
-    void ResultBuilder::captureExpectedException( Matchers::Impl::MatcherBase<std::string> const& matcher ) {
+    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);
+        }
 
-        assert( !isFalseTest( m_assertionInfo.resultDisposition ) );
-        AssertionResultData data = m_data;
-        data.resultType = ResultWas::Ok;
-        data.reconstructedExpression = capturedExpressionWithSecondArgument(m_assertionInfo.capturedExpression, m_assertionInfo.secondArg);
+        m_patternName.clear();
+        m_realPatternPos = 0;
 
-        std::string actualMessage = Catch::translateActiveException();
-        if( !matcher.match( actualMessage ) ) {
-            data.resultType = ResultWas::ExpressionFailed;
-            data.reconstructedExpression = actualMessage;
-        }
-        AssertionResult result( m_assertionInfo, data );
-        handleResult( result );
+        return token;
     }
 
-    void ResultBuilder::captureExpression() {
-        AssertionResult result = build();
-        handleResult( result );
+    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 ResultBuilder::handleResult( AssertionResult const& result )
-    {
-        getResultCapture().assertionEnded( result );
+    void TestSpecParser::addTagPattern() {
+        auto token = preprocessPattern();
 
-        if( !result.isOk() ) {
-            if( getCurrentContext().getConfig()->shouldDebugBreak() )
-                m_shouldDebugBreak = true;
-            if( getCurrentContext().getRunner()->aborting() || (m_assertionInfo.resultDisposition & ResultDisposition::Normal) )
-                m_shouldThrow = true;
+        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;
     }
 
-    void ResultBuilder::react() {
-#if defined(CATCH_CONFIG_FAST_COMPILE)
-        if (m_shouldDebugBreak) {
-            ///////////////////////////////////////////////////////////////////
-            // To inspect the state during test, you need to go one level up the callstack
-            // To go back to the test and change execution, jump over the throw statement
-            ///////////////////////////////////////////////////////////////////
-            CATCH_BREAK_INTO_DEBUGGER();
-        }
-#endif
-        if( m_shouldThrow )
-            throw Catch::TestFailureException();
+    TestSpec parseTestSpec( std::string const& arg ) {
+        return TestSpecParser( ITagAliasRegistry::get() ).parse( arg ).testSpec();
     }
 
-    bool ResultBuilder::shouldDebugBreak() const { return m_shouldDebugBreak; }
-    bool ResultBuilder::allowThrows() const { return getCurrentContext().getConfig()->allowThrows(); }
+} // namespace Catch
+// end catch_test_spec_parser.cpp
+// start catch_timer.cpp
 
-    AssertionResult ResultBuilder::build() const
-    {
-        return build( *this );
-    }
+#include <chrono>
 
-    // CAVEAT: The returned AssertionResult stores a pointer to the argument expr,
-    //         a temporary DecomposedExpression, which in turn holds references to
-    //         operands, possibly temporary as well.
-    //         It should immediately be passed to handleResult; if the expression
-    //         needs to be reported, its string expansion must be composed before
-    //         the temporaries are destroyed.
-    AssertionResult ResultBuilder::build( DecomposedExpression const& expr ) const
-    {
-        assert( m_data.resultType != ResultWas::Unknown );
-        AssertionResultData data = m_data;
+static const uint64_t nanosecondsInSecond = 1000000000;
 
-        if(m_usedStream)
-            data.message = m_stream().oss.str();
-        data.decomposedExpression = &expr; // for lazy reconstruction
-        return AssertionResult( m_assertionInfo, data );
+namespace Catch {
+
+    auto getCurrentNanosecondsSinceEpoch() -> uint64_t {
+        return std::chrono::duration_cast<std::chrono::nanoseconds>( std::chrono::high_resolution_clock::now().time_since_epoch() ).count();
     }
 
-    void ResultBuilder::reconstructExpression( std::string& dest ) const {
-        dest = capturedExpressionWithSecondArgument(m_assertionInfo.capturedExpression, m_assertionInfo.secondArg);
+    namespace {
+        auto estimateClockResolution() -> uint64_t {
+            uint64_t sum = 0;
+            static const uint64_t iterations = 1000000;
+
+            auto startTime = getCurrentNanosecondsSinceEpoch();
+
+            for( std::size_t i = 0; i < iterations; ++i ) {
+
+                uint64_t ticks;
+                uint64_t baseTicks = getCurrentNanosecondsSinceEpoch();
+                do {
+                    ticks = getCurrentNanosecondsSinceEpoch();
+                } while( ticks == baseTicks );
+
+                auto delta = ticks - baseTicks;
+                sum += delta;
+
+                // If we have been calibrating for over 3 seconds -- the clock
+                // 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 + 1u );
+                }
+            }
+
+            // We're just taking the mean, here. To do better we could take the std. dev and exclude outliers
+            // - and potentially do more iterations if there's a high variance.
+            return sum/iterations;
+        }
+    }
+    auto getEstimatedClockResolution() -> uint64_t {
+        static auto s_resolution = estimateClockResolution();
+        return s_resolution;
     }
 
-    void ResultBuilder::setExceptionGuard() {
-        m_guardException = true;
+    void Timer::start() {
+       m_nanoseconds = getCurrentNanosecondsSinceEpoch();
+    }
+    auto Timer::getElapsedNanoseconds() const -> uint64_t {
+        return getCurrentNanosecondsSinceEpoch() - m_nanoseconds;
     }
-    void ResultBuilder::unsetExceptionGuard() {
-        m_guardException = false;
+    auto Timer::getElapsedMicroseconds() const -> uint64_t {
+        return getElapsedNanoseconds()/1000;
+    }
+    auto Timer::getElapsedMilliseconds() const -> unsigned int {
+        return static_cast<unsigned int>(getElapsedMicroseconds()/1000);
+    }
+    auto Timer::getElapsedSeconds() const -> double {
+        return getElapsedMicroseconds()/1000000.0;
     }
 
-} // end namespace Catch
+} // namespace Catch
+// end catch_timer.cpp
+// start catch_tostring.cpp
+
+#if defined(__clang__)
+#    pragma clang diagnostic push
+#    pragma clang diagnostic ignored "-Wexit-time-destructors"
+#    pragma clang diagnostic ignored "-Wglobal-constructors"
+#endif
 
-// #included from: catch_tag_alias_registry.hpp
-#define TWOBLUECUBES_CATCH_TAG_ALIAS_REGISTRY_HPP_INCLUDED
+// Enable specific decls locally
+#if !defined(CATCH_CONFIG_ENABLE_CHRONO_STRINGMAKER)
+#define CATCH_CONFIG_ENABLE_CHRONO_STRINGMAKER
+#endif
+
+#include <cmath>
+#include <iomanip>
 
 namespace Catch {
 
-    TagAliasRegistry::~TagAliasRegistry() {}
+namespace Detail {
 
-    Option<TagAlias> TagAliasRegistry::find( std::string const& alias ) const {
-        std::map<std::string, TagAlias>::const_iterator it = m_registry.find( alias );
-        if( it != m_registry.end() )
-            return it->second;
-        else
-            return Option<TagAlias>();
-    }
+    const std::string unprintableString = "{?}";
 
-    std::string TagAliasRegistry::expandAliases( std::string const& unexpandedTestSpec ) const {
-        std::string expandedTestSpec = unexpandedTestSpec;
-        for( std::map<std::string, TagAlias>::const_iterator it = m_registry.begin(), itEnd = m_registry.end();
-                it != itEnd;
-                ++it ) {
-            std::size_t pos = expandedTestSpec.find( it->first );
-            if( pos != std::string::npos ) {
-                expandedTestSpec =  expandedTestSpec.substr( 0, pos ) +
-                                    it->second.tag +
-                                    expandedTestSpec.substr( pos + it->first.size() );
-            }
-        }
-        return expandedTestSpec;
-    }
+    namespace {
+        const int hexThreshold = 255;
 
-    void TagAliasRegistry::add( std::string const& alias, std::string const& tag, SourceLineInfo const& lineInfo ) {
+        struct Endianness {
+            enum Arch { Big, Little };
 
-        if( !startsWith( alias, "[@" ) || !endsWith( alias, ']' ) ) {
-            std::ostringstream oss;
-            oss << Colour( Colour::Red )
-                << "error: tag alias, \"" << alias << "\" is not of the form [@alias name].\n"
-                << Colour( Colour::FileName )
-                << lineInfo << '\n';
-            throw std::domain_error( oss.str().c_str() );
-        }
-        if( !m_registry.insert( std::make_pair( alias, TagAlias( tag, lineInfo ) ) ).second ) {
-            std::ostringstream oss;
-            oss << Colour( Colour::Red )
-                << "error: tag alias, \"" << alias << "\" already registered.\n"
-                << "\tFirst seen at "
-                << Colour( Colour::Red ) << find(alias)->lineInfo << '\n'
-                << Colour( Colour::Red ) << "\tRedefined at "
-                << Colour( Colour::FileName) << lineInfo << '\n';
-            throw std::domain_error( oss.str().c_str() );
-        }
+            static Arch which() {
+                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;
+            }
+        };
     }
 
-    ITagAliasRegistry::~ITagAliasRegistry() {}
+    std::string rawMemoryToString( const void *object, std::size_t size ) {
+        // Reverse order for little endian architectures
+        int i = 0, end = static_cast<int>( size ), inc = 1;
+        if( Endianness::which() == Endianness::Little ) {
+            i = end-1;
+            end = inc = -1;
+        }
 
-    ITagAliasRegistry const& ITagAliasRegistry::get() {
-        return getRegistryHub().getTagAliasRegistry();
+        unsigned char const *bytes = static_cast<unsigned char const *>(object);
+        ReusableStringStream rss;
+        rss << "0x" << std::setfill('0') << std::hex;
+        for( ; i != end; i += inc )
+             rss << std::setw(2) << static_cast<unsigned>(bytes[i]);
+       return rss.str();
     }
+}
 
-    RegistrarForTagAliases::RegistrarForTagAliases( char const* alias, char const* tag, SourceLineInfo const& lineInfo ) {
-        getMutableRegistryHub().registerTagAlias( alias, tag, lineInfo );
+template<typename T>
+std::string fpToString( T value, int precision ) {
+    if (Catch::isnan(value)) {
+        return "nan";
     }
 
-} // end namespace Catch
-
-// #included from: catch_matchers_string.hpp
+    ReusableStringStream rss;
+    rss << std::setprecision( precision )
+        << std::fixed
+        << value;
+    std::string d = rss.str();
+    std::size_t i = d.find_last_not_of( '0' );
+    if( i != std::string::npos && i != d.size()-1 ) {
+        if( d[i] == '.' )
+            i++;
+        d = d.substr( 0, i+1 );
+    }
+    return d;
+}
 
-namespace Catch {
-namespace Matchers {
+//// ======================================================= ////
+//
+//   Out-of-line defs for full specialization of StringMaker
+//
+//// ======================================================= ////
 
-    namespace StdString {
+std::string StringMaker<std::string>::convert(const std::string& str) {
+    if (!getCurrentContext().getConfig()->showInvisibles()) {
+        return '"' + str + '"';
+    }
 
-        CasedString::CasedString( std::string const& str, CaseSensitive::Choice caseSensitivity )
-        :   m_caseSensitivity( caseSensitivity ),
-            m_str( adjustString( str ) )
-        {}
-        std::string CasedString::adjustString( std::string const& str ) const {
-            return m_caseSensitivity == CaseSensitive::No
-                   ? toLower( str )
-                   : str;
-        }
-        std::string CasedString::caseSensitivitySuffix() const {
-            return m_caseSensitivity == CaseSensitive::No
-                   ? " (case insensitive)"
-                   : std::string();
+    std::string s("\"");
+    for (char c : str) {
+        switch (c) {
+        case '\n':
+            s.append("\\n");
+            break;
+        case '\t':
+            s.append("\\t");
+            break;
+        default:
+            s.push_back(c);
+            break;
         }
+    }
+    s.append("\"");
+    return s;
+}
 
-        StringMatcherBase::StringMatcherBase( std::string const& operation, CasedString const& comparator )
-        : m_comparator( comparator ),
-          m_operation( operation ) {
-        }
+#ifdef CATCH_CONFIG_CPP17_STRING_VIEW
+std::string StringMaker<std::string_view>::convert(std::string_view str) {
+    return ::Catch::Detail::stringify(std::string{ str });
+}
+#endif
 
-        std::string StringMatcherBase::describe() const {
-            std::string description;
-            description.reserve(5 + m_operation.size() + m_comparator.m_str.size() +
-                                        m_comparator.caseSensitivitySuffix().size());
-            description += m_operation;
-            description += ": \"";
-            description += m_comparator.m_str;
-            description += "\"";
-            description += m_comparator.caseSensitivitySuffix();
-            return description;
-        }
+std::string StringMaker<char const*>::convert(char const* str) {
+    if (str) {
+        return ::Catch::Detail::stringify(std::string{ str });
+    } else {
+        return{ "{null string}" };
+    }
+}
+std::string StringMaker<char*>::convert(char* str) {
+    if (str) {
+        return ::Catch::Detail::stringify(std::string{ str });
+    } else {
+        return{ "{null string}" };
+    }
+}
 
-        EqualsMatcher::EqualsMatcher( CasedString const& comparator ) : StringMatcherBase( "equals", comparator ) {}
+#ifdef CATCH_CONFIG_WCHAR
+std::string StringMaker<std::wstring>::convert(const std::wstring& wstr) {
+    std::string s;
+    s.reserve(wstr.size());
+    for (auto c : wstr) {
+        s += (c <= 0xff) ? static_cast<char>(c) : '?';
+    }
+    return ::Catch::Detail::stringify(s);
+}
 
-        bool EqualsMatcher::match( std::string const& source ) const {
-            return m_comparator.adjustString( source ) == m_comparator.m_str;
-        }
+# ifdef CATCH_CONFIG_CPP17_STRING_VIEW
+std::string StringMaker<std::wstring_view>::convert(std::wstring_view str) {
+    return StringMaker<std::wstring>::convert(std::wstring(str));
+}
+# endif
 
-        ContainsMatcher::ContainsMatcher( CasedString const& comparator ) : StringMatcherBase( "contains", comparator ) {}
+std::string StringMaker<wchar_t const*>::convert(wchar_t const * str) {
+    if (str) {
+        return ::Catch::Detail::stringify(std::wstring{ str });
+    } else {
+        return{ "{null string}" };
+    }
+}
+std::string StringMaker<wchar_t *>::convert(wchar_t * str) {
+    if (str) {
+        return ::Catch::Detail::stringify(std::wstring{ str });
+    } else {
+        return{ "{null string}" };
+    }
+}
+#endif
 
-        bool ContainsMatcher::match( std::string const& source ) const {
-            return contains( m_comparator.adjustString( source ), m_comparator.m_str );
-        }
+#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));
+}
+std::string StringMaker<long>::convert(long value) {
+    return ::Catch::Detail::stringify(static_cast<long long>(value));
+}
+std::string StringMaker<long long>::convert(long long value) {
+    ReusableStringStream rss;
+    rss << value;
+    if (value > Detail::hexThreshold) {
+        rss << " (0x" << std::hex << value << ')';
+    }
+    return rss.str();
+}
 
-        StartsWithMatcher::StartsWithMatcher( CasedString const& comparator ) : StringMatcherBase( "starts with", comparator ) {}
+std::string StringMaker<unsigned int>::convert(unsigned int value) {
+    return ::Catch::Detail::stringify(static_cast<unsigned long long>(value));
+}
+std::string StringMaker<unsigned long>::convert(unsigned long value) {
+    return ::Catch::Detail::stringify(static_cast<unsigned long long>(value));
+}
+std::string StringMaker<unsigned long long>::convert(unsigned long long value) {
+    ReusableStringStream rss;
+    rss << value;
+    if (value > Detail::hexThreshold) {
+        rss << " (0x" << std::hex << value << ')';
+    }
+    return rss.str();
+}
 
-        bool StartsWithMatcher::match( std::string const& source ) const {
-            return startsWith( m_comparator.adjustString( source ), m_comparator.m_str );
-        }
+std::string StringMaker<bool>::convert(bool b) {
+    return b ? "true" : "false";
+}
 
-        EndsWithMatcher::EndsWithMatcher( CasedString const& comparator ) : StringMatcherBase( "ends with", comparator ) {}
+std::string StringMaker<signed char>::convert(signed char value) {
+    if (value == '\r') {
+        return "'\\r'";
+    } else if (value == '\f') {
+        return "'\\f'";
+    } else if (value == '\n') {
+        return "'\\n'";
+    } else if (value == '\t') {
+        return "'\\t'";
+    } else if ('\0' <= value && value < ' ') {
+        return ::Catch::Detail::stringify(static_cast<unsigned int>(value));
+    } else {
+        char chstr[] = "' '";
+        chstr[1] = value;
+        return chstr;
+    }
+}
+std::string StringMaker<char>::convert(char c) {
+    return ::Catch::Detail::stringify(static_cast<signed char>(c));
+}
+std::string StringMaker<unsigned char>::convert(unsigned char c) {
+    return ::Catch::Detail::stringify(static_cast<char>(c));
+}
 
-        bool EndsWithMatcher::match( std::string const& source ) const {
-            return endsWith( m_comparator.adjustString( source ), m_comparator.m_str );
-        }
+std::string StringMaker<std::nullptr_t>::convert(std::nullptr_t) {
+    return "nullptr";
+}
 
-    } // namespace StdString
+int StringMaker<float>::precision = 5;
 
-    StdString::EqualsMatcher Equals( std::string const& str, CaseSensitive::Choice caseSensitivity ) {
-        return StdString::EqualsMatcher( StdString::CasedString( str, caseSensitivity) );
-    }
-    StdString::ContainsMatcher Contains( std::string const& str, CaseSensitive::Choice caseSensitivity ) {
-        return StdString::ContainsMatcher( StdString::CasedString( str, caseSensitivity) );
-    }
-    StdString::EndsWithMatcher EndsWith( std::string const& str, CaseSensitive::Choice caseSensitivity ) {
-        return StdString::EndsWithMatcher( StdString::CasedString( str, caseSensitivity) );
-    }
-    StdString::StartsWithMatcher StartsWith( std::string const& str, CaseSensitive::Choice caseSensitivity ) {
-        return StdString::StartsWithMatcher( StdString::CasedString( str, caseSensitivity) );
-    }
+std::string StringMaker<float>::convert(float value) {
+    return fpToString(value, precision) + 'f';
+}
 
-} // namespace Matchers
-} // namespace Catch
-// #included from: ../reporters/catch_reporter_multi.hpp
-#define TWOBLUECUBES_CATCH_REPORTER_MULTI_HPP_INCLUDED
+int StringMaker<double>::precision = 10;
 
-namespace Catch {
+std::string StringMaker<double>::convert(double value) {
+    return fpToString(value, precision);
+}
 
-class MultipleReporters : public SharedImpl<IStreamingReporter> {
-    typedef std::vector<Ptr<IStreamingReporter> > Reporters;
-    Reporters m_reporters;
+std::string ratio_string<std::atto>::symbol() { return "a"; }
+std::string ratio_string<std::femto>::symbol() { return "f"; }
+std::string ratio_string<std::pico>::symbol() { return "p"; }
+std::string ratio_string<std::nano>::symbol() { return "n"; }
+std::string ratio_string<std::micro>::symbol() { return "u"; }
+std::string ratio_string<std::milli>::symbol() { return "m"; }
 
-public:
-    void add( Ptr<IStreamingReporter> const& reporter ) {
-        m_reporters.push_back( reporter );
-    }
+} // end namespace Catch
 
-public: // IStreamingReporter
+#if defined(__clang__)
+#    pragma clang diagnostic pop
+#endif
 
-    virtual ReporterPreferences getPreferences() const CATCH_OVERRIDE {
-        return m_reporters[0]->getPreferences();
-    }
+// end catch_tostring.cpp
+// start catch_totals.cpp
 
-    virtual void noMatchingTestCases( std::string const& spec ) CATCH_OVERRIDE {
-        for( Reporters::const_iterator it = m_reporters.begin(), itEnd = m_reporters.end();
-                it != itEnd;
-                ++it )
-            (*it)->noMatchingTestCases( spec );
-    }
+namespace Catch {
 
-    virtual void testRunStarting( TestRunInfo const& testRunInfo ) CATCH_OVERRIDE {
-        for( Reporters::const_iterator it = m_reporters.begin(), itEnd = m_reporters.end();
-                it != itEnd;
-                ++it )
-            (*it)->testRunStarting( testRunInfo );
+    Counts Counts::operator - ( Counts const& other ) const {
+        Counts diff;
+        diff.passed = passed - other.passed;
+        diff.failed = failed - other.failed;
+        diff.failedButOk = failedButOk - other.failedButOk;
+        return diff;
     }
 
-    virtual void testGroupStarting( GroupInfo const& groupInfo ) CATCH_OVERRIDE {
-        for( Reporters::const_iterator it = m_reporters.begin(), itEnd = m_reporters.end();
-                it != itEnd;
-                ++it )
-            (*it)->testGroupStarting( groupInfo );
+    Counts& Counts::operator += ( Counts const& other ) {
+        passed += other.passed;
+        failed += other.failed;
+        failedButOk += other.failedButOk;
+        return *this;
     }
 
-    virtual void testCaseStarting( TestCaseInfo const& testInfo ) CATCH_OVERRIDE {
-        for( Reporters::const_iterator it = m_reporters.begin(), itEnd = m_reporters.end();
-                it != itEnd;
-                ++it )
-            (*it)->testCaseStarting( testInfo );
+    std::size_t Counts::total() const {
+        return passed + failed + failedButOk;
     }
-
-    virtual void sectionStarting( SectionInfo const& sectionInfo ) CATCH_OVERRIDE {
-        for( Reporters::const_iterator it = m_reporters.begin(), itEnd = m_reporters.end();
-                it != itEnd;
-                ++it )
-            (*it)->sectionStarting( sectionInfo );
+    bool Counts::allPassed() const {
+        return failed == 0 && failedButOk == 0;
     }
-
-    virtual void assertionStarting( AssertionInfo const& assertionInfo ) CATCH_OVERRIDE {
-        for( Reporters::const_iterator it = m_reporters.begin(), itEnd = m_reporters.end();
-                it != itEnd;
-                ++it )
-            (*it)->assertionStarting( assertionInfo );
+    bool Counts::allOk() const {
+        return failed == 0;
     }
 
-    // The return value indicates if the messages buffer should be cleared:
-    virtual bool assertionEnded( AssertionStats const& assertionStats ) CATCH_OVERRIDE {
-        bool clearBuffer = false;
-        for( Reporters::const_iterator it = m_reporters.begin(), itEnd = m_reporters.end();
-                it != itEnd;
-                ++it )
-            clearBuffer |= (*it)->assertionEnded( assertionStats );
-        return clearBuffer;
+    Totals Totals::operator - ( Totals const& other ) const {
+        Totals diff;
+        diff.assertions = assertions - other.assertions;
+        diff.testCases = testCases - other.testCases;
+        return diff;
     }
 
-    virtual void sectionEnded( SectionStats const& sectionStats ) CATCH_OVERRIDE {
-        for( Reporters::const_iterator it = m_reporters.begin(), itEnd = m_reporters.end();
-                it != itEnd;
-                ++it )
-            (*it)->sectionEnded( sectionStats );
+    Totals& Totals::operator += ( Totals const& other ) {
+        assertions += other.assertions;
+        testCases += other.testCases;
+        return *this;
     }
 
-    virtual void testCaseEnded( TestCaseStats const& testCaseStats ) CATCH_OVERRIDE {
-        for( Reporters::const_iterator it = m_reporters.begin(), itEnd = m_reporters.end();
-                it != itEnd;
-                ++it )
-            (*it)->testCaseEnded( testCaseStats );
+    Totals Totals::delta( Totals const& prevTotals ) const {
+        Totals diff = *this - prevTotals;
+        if( diff.assertions.failed > 0 )
+            ++diff.testCases.failed;
+        else if( diff.assertions.failedButOk > 0 )
+            ++diff.testCases.failedButOk;
+        else
+            ++diff.testCases.passed;
+        return diff;
     }
 
-    virtual void testGroupEnded( TestGroupStats const& testGroupStats ) CATCH_OVERRIDE {
-        for( Reporters::const_iterator it = m_reporters.begin(), itEnd = m_reporters.end();
-                it != itEnd;
-                ++it )
-            (*it)->testGroupEnded( testGroupStats );
-    }
+}
+// end catch_totals.cpp
+// start catch_uncaught_exceptions.cpp
 
-    virtual void testRunEnded( TestRunStats const& testRunStats ) CATCH_OVERRIDE {
-        for( Reporters::const_iterator it = m_reporters.begin(), itEnd = m_reporters.end();
-                it != itEnd;
-                ++it )
-            (*it)->testRunEnded( testRunStats );
-    }
+// start catch_config_uncaught_exceptions.hpp
 
-    virtual void skipTest( TestCaseInfo const& testInfo ) CATCH_OVERRIDE {
-        for( Reporters::const_iterator it = m_reporters.begin(), itEnd = m_reporters.end();
-                it != itEnd;
-                ++it )
-            (*it)->skipTest( testInfo );
-    }
+//              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)
 
-    virtual MultipleReporters* tryAsMulti() CATCH_OVERRIDE {
-        return this;
-    }
+// SPDX-License-Identifier: BSL-1.0
 
-};
+#ifndef CATCH_CONFIG_UNCAUGHT_EXCEPTIONS_HPP
+#define CATCH_CONFIG_UNCAUGHT_EXCEPTIONS_HPP
 
-Ptr<IStreamingReporter> addReporter( Ptr<IStreamingReporter> const& existingReporter, Ptr<IStreamingReporter> const& additionalReporter ) {
-    Ptr<IStreamingReporter> resultingReporter;
+#if defined(_MSC_VER)
+#  if _MSC_VER >= 1900 // Visual Studio 2015 or newer
+#    define CATCH_INTERNAL_CONFIG_CPP17_UNCAUGHT_EXCEPTIONS
+#  endif
+#endif
 
-    if( existingReporter ) {
-        MultipleReporters* multi = existingReporter->tryAsMulti();
-        if( !multi ) {
-            multi = new MultipleReporters;
-            resultingReporter = Ptr<IStreamingReporter>( multi );
-            if( existingReporter )
-                multi->add( existingReporter );
-        }
-        else
-            resultingReporter = existingReporter;
-        multi->add( additionalReporter );
-    }
-    else
-        resultingReporter = additionalReporter;
+#include <exception>
 
-    return resultingReporter;
-}
+#if defined(__cpp_lib_uncaught_exceptions) \
+    && !defined(CATCH_INTERNAL_CONFIG_CPP17_UNCAUGHT_EXCEPTIONS)
 
-} // end namespace Catch
+#  define CATCH_INTERNAL_CONFIG_CPP17_UNCAUGHT_EXCEPTIONS
+#endif // __cpp_lib_uncaught_exceptions
 
-// #included from: ../reporters/catch_reporter_xml.hpp
-#define TWOBLUECUBES_CATCH_REPORTER_XML_HPP_INCLUDED
+#if defined(CATCH_INTERNAL_CONFIG_CPP17_UNCAUGHT_EXCEPTIONS) \
+    && !defined(CATCH_CONFIG_NO_CPP17_UNCAUGHT_EXCEPTIONS) \
+    && !defined(CATCH_CONFIG_CPP17_UNCAUGHT_EXCEPTIONS)
 
-// #included from: catch_reporter_bases.hpp
-#define TWOBLUECUBES_CATCH_REPORTER_BASES_HPP_INCLUDED
+#  define CATCH_CONFIG_CPP17_UNCAUGHT_EXCEPTIONS
+#endif
 
-#include <cstring>
-#include <cfloat>
-#include <cstdio>
-#include <assert.h>
+#endif // CATCH_CONFIG_UNCAUGHT_EXCEPTIONS_HPP
+// end catch_config_uncaught_exceptions.hpp
+#include <exception>
 
 namespace Catch {
-
-    namespace {
-        // Because formatting using c++ streams is stateful, drop down to C is required
-        // Alternatively we could use stringstream, but its performance is... not good.
-        std::string getFormattedDuration( double duration ) {
-            // Max exponent + 1 is required to represent the whole part
-            // + 1 for decimal point
-            // + 3 for the 3 decimal places
-            // + 1 for null terminator
-            const size_t maxDoubleSize = DBL_MAX_10_EXP + 1 + 1 + 3 + 1;
-            char buffer[maxDoubleSize];
-
-            // Save previous errno, to prevent sprintf from overwriting it
-            ErrnoGuard guard;
-#ifdef _MSC_VER
-            sprintf_s(buffer, "%.3f", duration);
+    bool uncaught_exceptions() {
+#if defined(CATCH_CONFIG_DISABLE_EXCEPTIONS)
+        return false;
+#elif defined(CATCH_CONFIG_CPP17_UNCAUGHT_EXCEPTIONS)
+        return std::uncaught_exceptions() > 0;
 #else
-            sprintf(buffer, "%.3f", duration);
+        return std::uncaught_exception();
 #endif
-            return std::string(buffer);
+  }
+} // end namespace Catch
+// end catch_uncaught_exceptions.cpp
+// start catch_version.cpp
+
+#include <ostream>
+
+namespace Catch {
+
+    Version::Version
+        (   unsigned int _majorVersion,
+            unsigned int _minorVersion,
+            unsigned int _patchNumber,
+            char const * const _branchName,
+            unsigned int _buildNumber )
+    :   majorVersion( _majorVersion ),
+        minorVersion( _minorVersion ),
+        patchNumber( _patchNumber ),
+        branchName( _branchName ),
+        buildNumber( _buildNumber )
+    {}
+
+    std::ostream& operator << ( std::ostream& os, Version const& version ) {
+        os  << version.majorVersion << '.'
+            << version.minorVersion << '.'
+            << version.patchNumber;
+        // branchName is never null -> 0th char is \0 if it is empty
+        if (version.branchName[0]) {
+            os << '-' << version.branchName
+               << '.' << version.buildNumber;
         }
+        return os;
     }
 
-    struct StreamingReporterBase : SharedImpl<IStreamingReporter> {
+    Version const& libraryVersion() {
+        static Version version( 2, 13, 10, "", 0 );
+        return version;
+    }
 
-        StreamingReporterBase( ReporterConfig const& _config )
-        :   m_config( _config.fullConfig() ),
-            stream( _config.stream() )
-        {
-            m_reporterPrefs.shouldRedirectStdOut = false;
+}
+// end catch_version.cpp
+// start catch_wildcard_pattern.cpp
+
+namespace Catch {
+
+    WildcardPattern::WildcardPattern( std::string const& pattern,
+                                      CaseSensitive::Choice caseSensitivity )
+    :   m_caseSensitivity( caseSensitivity ),
+        m_pattern( normaliseString( pattern ) )
+    {
+        if( startsWith( m_pattern, '*' ) ) {
+            m_pattern = m_pattern.substr( 1 );
+            m_wildcard = WildcardAtStart;
         }
+        if( endsWith( m_pattern, '*' ) ) {
+            m_pattern = m_pattern.substr( 0, m_pattern.size()-1 );
+            m_wildcard = static_cast<WildcardPosition>( m_wildcard | WildcardAtEnd );
+        }
+    }
 
-        virtual ReporterPreferences getPreferences() const CATCH_OVERRIDE {
-            return m_reporterPrefs;
+    bool WildcardPattern::matches( std::string const& str ) const {
+        switch( m_wildcard ) {
+            case NoWildcard:
+                return m_pattern == normaliseString( str );
+            case WildcardAtStart:
+                return endsWith( normaliseString( str ), m_pattern );
+            case WildcardAtEnd:
+                return startsWith( normaliseString( str ), m_pattern );
+            case WildcardAtBothEnds:
+                return contains( normaliseString( str ), m_pattern );
+            default:
+                CATCH_INTERNAL_ERROR( "Unknown enum" );
         }
+    }
+
+    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>
+#include <type_traits>
 
-        virtual ~StreamingReporterBase() CATCH_OVERRIDE;
+namespace Catch {
 
-        virtual void noMatchingTestCases( std::string const& ) CATCH_OVERRIDE {}
+namespace {
 
-        virtual void testRunStarting( TestRunInfo const& _testRunInfo ) CATCH_OVERRIDE {
-            currentTestRunInfo = _testRunInfo;
-        }
-        virtual void testGroupStarting( GroupInfo const& _groupInfo ) CATCH_OVERRIDE {
-            currentGroupInfo = _groupInfo;
+    size_t trailingBytes(unsigned char c) {
+        if ((c & 0xE0) == 0xC0) {
+            return 2;
         }
-
-        virtual void testCaseStarting( TestCaseInfo const& _testInfo ) CATCH_OVERRIDE {
-            currentTestCaseInfo = _testInfo;
+        if ((c & 0xF0) == 0xE0) {
+            return 3;
         }
-        virtual void sectionStarting( SectionInfo const& _sectionInfo ) CATCH_OVERRIDE {
-            m_sectionStack.push_back( _sectionInfo );
+        if ((c & 0xF8) == 0xF0) {
+            return 4;
         }
+        CATCH_INTERNAL_ERROR("Invalid multibyte utf-8 start byte encountered");
+    }
 
-        virtual void sectionEnded( SectionStats const& /* _sectionStats */ ) CATCH_OVERRIDE {
-            m_sectionStack.pop_back();
-        }
-        virtual void testCaseEnded( TestCaseStats const& /* _testCaseStats */ ) CATCH_OVERRIDE {
-            currentTestCaseInfo.reset();
+    uint32_t headerValue(unsigned char c) {
+        if ((c & 0xE0) == 0xC0) {
+            return c & 0x1F;
         }
-        virtual void testGroupEnded( TestGroupStats const& /* _testGroupStats */ ) CATCH_OVERRIDE {
-            currentGroupInfo.reset();
+        if ((c & 0xF0) == 0xE0) {
+            return c & 0x0F;
         }
-        virtual void testRunEnded( TestRunStats const& /* _testRunStats */ ) CATCH_OVERRIDE {
-            currentTestCaseInfo.reset();
-            currentGroupInfo.reset();
-            currentTestRunInfo.reset();
+        if ((c & 0xF8) == 0xF0) {
+            return c & 0x07;
         }
+        CATCH_INTERNAL_ERROR("Invalid multibyte utf-8 start byte encountered");
+    }
 
-        virtual void skipTest( TestCaseInfo const& ) CATCH_OVERRIDE {
-            // Don't do anything with this by default.
-            // It can optionally be overridden in the derived class.
-        }
+    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);
+    }
 
-        Ptr<IConfig const> m_config;
-        std::ostream& stream;
+    bool shouldNewline(XmlFormatting fmt) {
+        return !!(static_cast<std::underlying_type<XmlFormatting>::type>(fmt & XmlFormatting::Newline));
+    }
 
-        LazyStat<TestRunInfo> currentTestRunInfo;
-        LazyStat<GroupInfo> currentGroupInfo;
-        LazyStat<TestCaseInfo> currentTestCaseInfo;
+    bool shouldIndent(XmlFormatting fmt) {
+        return !!(static_cast<std::underlying_type<XmlFormatting>::type>(fmt & XmlFormatting::Indent));
+    }
 
-        std::vector<SectionInfo> m_sectionStack;
-        ReporterPreferences m_reporterPrefs;
-    };
+} // anonymous namespace
 
-    struct CumulativeReporterBase : SharedImpl<IStreamingReporter> {
-        template<typename T, typename ChildNodeT>
-        struct Node : SharedImpl<> {
-            explicit Node( T const& _value ) : value( _value ) {}
-            virtual ~Node() {}
+    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)
+        );
+    }
 
-            typedef std::vector<Ptr<ChildNodeT> > ChildNodes;
-            T value;
-            ChildNodes children;
-        };
-        struct SectionNode : SharedImpl<> {
-            explicit SectionNode( SectionStats const& _stats ) : stats( _stats ) {}
-            virtual ~SectionNode();
+    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)
+        );
+    }
 
-            bool operator == ( SectionNode const& other ) const {
-                return stats.sectionInfo.lineInfo == other.stats.sectionInfo.lineInfo;
-            }
-            bool operator == ( Ptr<SectionNode> const& other ) const {
-                return operator==( *other );
-            }
+    XmlEncode::XmlEncode( std::string const& str, ForWhat forWhat )
+    :   m_str( str ),
+        m_forWhat( forWhat )
+    {}
 
-            SectionStats stats;
-            typedef std::vector<Ptr<SectionNode> > ChildSections;
-            typedef std::vector<AssertionStats> Assertions;
-            ChildSections childSections;
-            Assertions assertions;
-            std::string stdOut;
-            std::string stdErr;
-        };
+    void XmlEncode::encodeTo( std::ostream& os ) const {
+        // Apostrophe escaping not necessary if we always use " to write attributes
+        // (see: http://www.w3.org/TR/xml/#syntax)
 
-        struct BySectionInfo {
-            BySectionInfo( SectionInfo const& other ) : m_other( other ) {}
-            BySectionInfo( BySectionInfo const& other ) : m_other( other.m_other ) {}
-            bool operator() ( Ptr<SectionNode> const& node ) const {
-                return ((node->stats.sectionInfo.name == m_other.name) &&
-                        (node->stats.sectionInfo.lineInfo == m_other.lineInfo));
+        for( std::size_t idx = 0; idx < m_str.size(); ++ idx ) {
+            unsigned char c = m_str[idx];
+            switch (c) {
+            case '<':   os << "&lt;"; break;
+            case '&':   os << "&amp;"; break;
+
+            case '>':
+                // See: http://www.w3.org/TR/xml/#syntax
+                if (idx > 2 && m_str[idx - 1] == ']' && m_str[idx - 2] == ']')
+                    os << "&gt;";
+                else
+                    os << c;
+                break;
+
+            case '\"':
+                if (m_forWhat == ForAttributes)
+                    os << "&quot;";
+                else
+                    os << c;
+                break;
+
+            default:
+                // Check for control characters and invalid utf-8
+
+                // Escape control characters in standard ascii
+                // see http://stackoverflow.com/questions/404107/why-are-control-characters-illegal-in-xml-1-0
+                if (c < 0x09 || (c > 0x0D && c < 0x20) || c == 0x7F) {
+                    hexEscapeChar(os, c);
+                    break;
+                }
+
+                // Plain ASCII: Write it to stream
+                if (c < 0x7F) {
+                    os << c;
+                    break;
+                }
+
+                // UTF-8 territory
+                // Check if the encoding is valid and if it is not, hex escape bytes.
+                // Important: We do not check the exact decoded values for validity, only the encoding format
+                // First check that this bytes is a valid lead byte:
+                // This means that it is not encoded as 1111 1XXX
+                // Or as 10XX XXXX
+                if (c <  0xC0 ||
+                    c >= 0xF8) {
+                    hexEscapeChar(os, c);
+                    break;
+                }
+
+                auto encBytes = trailingBytes(c);
+                // Are there enough bytes left to avoid accessing out-of-bounds memory?
+                if (idx + encBytes - 1 >= m_str.size()) {
+                    hexEscapeChar(os, c);
+                    break;
+                }
+                // The header is valid, check data
+                // The next encBytes bytes must together be a valid utf-8
+                // This means: bitpattern 10XX XXXX and the extracted value is sane (ish)
+                bool valid = true;
+                uint32_t value = headerValue(c);
+                for (std::size_t n = 1; n < encBytes; ++n) {
+                    unsigned char nc = m_str[idx + n];
+                    valid &= ((nc & 0xC0) == 0x80);
+                    value = (value << 6) | (nc & 0x3F);
+                }
+
+                if (
+                    // Wrong bit pattern of following bytes
+                    (!valid) ||
+                    // Overlong encodings
+                    (value < 0x80) ||
+                    (0x80 <= value && value < 0x800   && encBytes > 2) ||
+                    (0x800 < value && value < 0x10000 && encBytes > 3) ||
+                    // Encoded value out of range
+                    (value >= 0x110000)
+                    ) {
+                    hexEscapeChar(os, c);
+                    break;
+                }
+
+                // If we got here, this is in fact a valid(ish) utf-8 sequence
+                for (std::size_t n = 0; n < encBytes; ++n) {
+                    os << m_str[idx + n];
+                }
+                idx += encBytes - 1;
+                break;
             }
-        private:
-            void operator=( BySectionInfo const& );
-            SectionInfo const& m_other;
-        };
+        }
+    }
 
-        typedef Node<TestCaseStats, SectionNode> TestCaseNode;
-        typedef Node<TestGroupStats, TestCaseNode> TestGroupNode;
-        typedef Node<TestRunStats, TestGroupNode> TestRunNode;
+    std::ostream& operator << ( std::ostream& os, XmlEncode const& xmlEncode ) {
+        xmlEncode.encodeTo( os );
+        return os;
+    }
 
-        CumulativeReporterBase( ReporterConfig const& _config )
-        :   m_config( _config.fullConfig() ),
-            stream( _config.stream() )
-        {
-            m_reporterPrefs.shouldRedirectStdOut = false;
+    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_fmt(other.m_fmt)
+    {
+        other.m_writer = nullptr;
+        other.m_fmt = XmlFormatting::None;
+    }
+    XmlWriter::ScopedElement& XmlWriter::ScopedElement::operator=( ScopedElement&& other ) noexcept {
+        if ( m_writer ) {
+            m_writer->endElement();
         }
-        ~CumulativeReporterBase();
+        m_writer = other.m_writer;
+        other.m_writer = nullptr;
+        m_fmt = other.m_fmt;
+        other.m_fmt = XmlFormatting::None;
+        return *this;
+    }
 
-        virtual ReporterPreferences getPreferences() const CATCH_OVERRIDE {
-            return m_reporterPrefs;
+    XmlWriter::ScopedElement::~ScopedElement() {
+        if (m_writer) {
+            m_writer->endElement(m_fmt);
         }
+    }
 
-        virtual void testRunStarting( TestRunInfo const& ) CATCH_OVERRIDE {}
-        virtual void testGroupStarting( GroupInfo const& ) CATCH_OVERRIDE {}
+    XmlWriter::ScopedElement& XmlWriter::ScopedElement::writeText( std::string const& text, XmlFormatting fmt ) {
+        m_writer->writeText( text, fmt );
+        return *this;
+    }
 
-        virtual void testCaseStarting( TestCaseInfo const& ) CATCH_OVERRIDE {}
+    XmlWriter::XmlWriter( std::ostream& os ) : m_os( os )
+    {
+        writeDeclaration();
+    }
 
-        virtual void sectionStarting( SectionInfo const& sectionInfo ) CATCH_OVERRIDE {
-            SectionStats incompleteStats( sectionInfo, Counts(), 0, false );
-            Ptr<SectionNode> node;
-            if( m_sectionStack.empty() ) {
-                if( !m_rootSection )
-                    m_rootSection = new SectionNode( incompleteStats );
-                node = m_rootSection;
-            }
-            else {
-                SectionNode& parentNode = *m_sectionStack.back();
-                SectionNode::ChildSections::const_iterator it =
-                    std::find_if(   parentNode.childSections.begin(),
-                                    parentNode.childSections.end(),
-                                    BySectionInfo( sectionInfo ) );
-                if( it == parentNode.childSections.end() ) {
-                    node = new SectionNode( incompleteStats );
-                    parentNode.childSections.push_back( node );
-                }
-                else
-                    node = *it;
+    XmlWriter::~XmlWriter() {
+        while (!m_tags.empty()) {
+            endElement();
+        }
+        newlineIfNecessary();
+    }
+
+    XmlWriter& XmlWriter::startElement( std::string const& name, XmlFormatting fmt ) {
+        ensureTagClosed();
+        newlineIfNecessary();
+        if (shouldIndent(fmt)) {
+            m_os << m_indent;
+            m_indent += "  ";
+        }
+        m_os << '<' << name;
+        m_tags.push_back( name );
+        m_tagIsOpen = true;
+        applyFormatting(fmt);
+        return *this;
+    }
+
+    XmlWriter::ScopedElement XmlWriter::scopedElement( std::string const& name, XmlFormatting fmt ) {
+        ScopedElement scoped( this, fmt );
+        startElement( name, fmt );
+        return scoped;
+    }
+
+    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_sectionStack.push_back( node );
-            m_deepestSection = node;
+            m_os << "</" << m_tags.back() << ">";
         }
+        m_os << std::flush;
+        applyFormatting(fmt);
+        m_tags.pop_back();
+        return *this;
+    }
 
-        virtual void assertionStarting( AssertionInfo const& ) CATCH_OVERRIDE {}
+    XmlWriter& XmlWriter::writeAttribute( std::string const& name, std::string const& attribute ) {
+        if( !name.empty() && !attribute.empty() )
+            m_os << ' ' << name << "=\"" << XmlEncode( attribute, XmlEncode::ForAttributes ) << '"';
+        return *this;
+    }
 
-        virtual bool assertionEnded( AssertionStats const& assertionStats ) CATCH_OVERRIDE {
-            assert( !m_sectionStack.empty() );
-            SectionNode& sectionNode = *m_sectionStack.back();
-            sectionNode.assertions.push_back( assertionStats );
-            // AssertionResult holds a pointer to a temporary DecomposedExpression,
-            // which getExpandedExpression() calls to build the expression string.
-            // Our section stack copy of the assertionResult will likely outlive the
-            // temporary, so it must be expanded or discarded now to avoid calling
-            // a destroyed object later.
-            prepareExpandedExpression( sectionNode.assertions.back().assertionResult );
-            return true;
-        }
-        virtual void sectionEnded( SectionStats const& sectionStats ) CATCH_OVERRIDE {
-            assert( !m_sectionStack.empty() );
-            SectionNode& node = *m_sectionStack.back();
-            node.stats = sectionStats;
-            m_sectionStack.pop_back();
-        }
-        virtual void testCaseEnded( TestCaseStats const& testCaseStats ) CATCH_OVERRIDE {
-            Ptr<TestCaseNode> node = new TestCaseNode( testCaseStats );
-            assert( m_sectionStack.size() == 0 );
-            node->children.push_back( m_rootSection );
-            m_testCases.push_back( node );
-            m_rootSection.reset();
+    XmlWriter& XmlWriter::writeAttribute( std::string const& name, bool attribute ) {
+        m_os << ' ' << name << "=\"" << ( attribute ? "true" : "false" ) << '"';
+        return *this;
+    }
 
-            assert( m_deepestSection );
-            m_deepestSection->stdOut = testCaseStats.stdOut;
-            m_deepestSection->stdErr = testCaseStats.stdErr;
+    XmlWriter& XmlWriter::writeText( std::string const& text, XmlFormatting fmt) {
+        if( !text.empty() ){
+            bool tagWasOpen = m_tagIsOpen;
+            ensureTagClosed();
+            if (tagWasOpen && shouldIndent(fmt)) {
+                m_os << m_indent;
+            }
+            m_os << XmlEncode( text );
+            applyFormatting(fmt);
         }
-        virtual void testGroupEnded( TestGroupStats const& testGroupStats ) CATCH_OVERRIDE {
-            Ptr<TestGroupNode> node = new TestGroupNode( testGroupStats );
-            node->children.swap( m_testCases );
-            m_testGroups.push_back( node );
+        return *this;
+    }
+
+    XmlWriter& XmlWriter::writeComment( std::string const& text, XmlFormatting fmt) {
+        ensureTagClosed();
+        if (shouldIndent(fmt)) {
+            m_os << m_indent;
         }
-        virtual void testRunEnded( TestRunStats const& testRunStats ) CATCH_OVERRIDE {
-            Ptr<TestRunNode> node = new TestRunNode( testRunStats );
-            node->children.swap( m_testGroups );
-            m_testRuns.push_back( node );
-            testRunEndedCumulative();
+        m_os << "<!--" << text << "-->";
+        applyFormatting(fmt);
+        return *this;
+    }
+
+    void XmlWriter::writeStylesheetRef( std::string const& url ) {
+        m_os << "<?xml-stylesheet type=\"text/xsl\" href=\"" << url << "\"?>\n";
+    }
+
+    XmlWriter& XmlWriter::writeBlankLine() {
+        ensureTagClosed();
+        m_os << '\n';
+        return *this;
+    }
+
+    void XmlWriter::ensureTagClosed() {
+        if( m_tagIsOpen ) {
+            m_os << '>' << std::flush;
+            newlineIfNecessary();
+            m_tagIsOpen = false;
         }
-        virtual void testRunEndedCumulative() = 0;
+    }
 
-        virtual void skipTest( TestCaseInfo const& ) CATCH_OVERRIDE {}
+    void XmlWriter::applyFormatting(XmlFormatting fmt) {
+        m_needsNewline = shouldNewline(fmt);
+    }
 
-        virtual void prepareExpandedExpression( AssertionResult& result ) const {
-            if( result.isOk() )
-                result.discardDecomposedExpression();
-            else
-                result.expandDecomposedExpression();
+    void XmlWriter::writeDeclaration() {
+        m_os << "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n";
+    }
+
+    void XmlWriter::newlineIfNecessary() {
+        if( m_needsNewline ) {
+            m_os << std::endl;
+            m_needsNewline = false;
         }
+    }
+}
+// end catch_xmlwriter.cpp
+// start catch_reporter_bases.cpp
 
-        Ptr<IConfig const> m_config;
-        std::ostream& stream;
-        std::vector<AssertionStats> m_assertions;
-        std::vector<std::vector<Ptr<SectionNode> > > m_sections;
-        std::vector<Ptr<TestCaseNode> > m_testCases;
-        std::vector<Ptr<TestGroupNode> > m_testGroups;
+#include <cstring>
+#include <cfloat>
+#include <cstdio>
+#include <cassert>
+#include <memory>
 
-        std::vector<Ptr<TestRunNode> > m_testRuns;
+namespace Catch {
+    void prepareExpandedExpression(AssertionResult& result) {
+        result.getExpandedExpression();
+    }
 
-        Ptr<SectionNode> m_rootSection;
-        Ptr<SectionNode> m_deepestSection;
-        std::vector<Ptr<SectionNode> > m_sectionStack;
-        ReporterPreferences m_reporterPrefs;
+    // Because formatting using c++ streams is stateful, drop down to C is required
+    // Alternatively we could use stringstream, but its performance is... not good.
+    std::string getFormattedDuration( double duration ) {
+        // Max exponent + 1 is required to represent the whole part
+        // + 1 for decimal point
+        // + 3 for the 3 decimal places
+        // + 1 for null terminator
+        const std::size_t maxDoubleSize = DBL_MAX_10_EXP + 1 + 1 + 3 + 1;
+        char buffer[maxDoubleSize];
 
-    };
+        // Save previous errno, to prevent sprintf from overwriting it
+        ErrnoGuard guard;
+#ifdef _MSC_VER
+        sprintf_s(buffer, "%.3f", duration);
+#else
+        std::sprintf(buffer, "%.3f", duration);
+#endif
+        return std::string(buffer);
+    }
 
-    template<char C>
-    char const* getLineOfChars() {
-        static char line[CATCH_CONFIG_CONSOLE_WIDTH] = {0};
-        if( !*line ) {
-            std::memset( line, C, CATCH_CONFIG_CONSOLE_WIDTH-1 );
-            line[CATCH_CONFIG_CONSOLE_WIDTH-1] = 0;
+    bool shouldShowDuration( IConfig const& config, double duration ) {
+        if ( config.showDurations() == ShowDurations::Always ) {
+            return true;
         }
-        return line;
+        if ( config.showDurations() == ShowDurations::Never ) {
+            return false;
+        }
+        const double min = config.minDuration();
+        return min >= 0 && duration >= min;
     }
 
-    struct TestEventListenerBase : StreamingReporterBase {
-        TestEventListenerBase( ReporterConfig const& _config )
-        :   StreamingReporterBase( _config )
-        {}
+    std::string serializeFilters( std::vector<std::string> const& container ) {
+        ReusableStringStream oss;
+        bool first = true;
+        for (auto&& filter : container)
+        {
+            if (!first)
+                oss << ' ';
+            else
+                first = false;
 
-        virtual void assertionStarting( AssertionInfo const& ) CATCH_OVERRIDE {}
-        virtual bool assertionEnded( AssertionStats const& ) CATCH_OVERRIDE {
-            return false;
+            oss << filter;
         }
-    };
+        return oss.str();
+    }
+
+    TestEventListenerBase::TestEventListenerBase(ReporterConfig const & _config)
+        :StreamingReporterBase(_config) {}
+
+    std::set<Verbosity> TestEventListenerBase::getSupportedVerbosities() {
+        return { Verbosity::Quiet, Verbosity::Normal, Verbosity::High };
+    }
+
+    void TestEventListenerBase::assertionStarting(AssertionInfo const &) {}
+
+    bool TestEventListenerBase::assertionEnded(AssertionStats const &) {
+        return false;
+    }
 
 } // end namespace Catch
+// end catch_reporter_bases.cpp
+// start catch_reporter_compact.cpp
 
-// #included from: ../internal/catch_reporter_registrars.hpp
-#define TWOBLUECUBES_CATCH_REPORTER_REGISTRARS_HPP_INCLUDED
+namespace {
 
-namespace Catch {
+#ifdef CATCH_PLATFORM_MAC
+    const char* failedString() { return "FAILED"; }
+    const char* passedString() { return "PASSED"; }
+#else
+    const char* failedString() { return "failed"; }
+    const char* passedString() { return "passed"; }
+#endif
 
-    template<typename T>
-    class LegacyReporterRegistrar {
+    // Colour::LightGrey
+    Catch::Colour::Code dimColour() { return Catch::Colour::FileName; }
 
-        class ReporterFactory : public IReporterFactory {
-            virtual IStreamingReporter* create( ReporterConfig const& config ) const {
-                return new LegacyReporterAdapter( new T( config ) );
-            }
+    std::string bothOrAll( std::size_t count ) {
+        return count == 1 ? std::string() :
+               count == 2 ? "both " : "all " ;
+    }
 
-            virtual std::string getDescription() const {
-                return T::getDescription();
-            }
-        };
+} // anon namespace
 
-    public:
+namespace Catch {
+namespace {
+// Colour, message variants:
+// - white: No tests ran.
+// -   red: Failed [both/all] N test cases, failed [both/all] M assertions.
+// - white: Passed [both/all] N test cases (no assertions).
+// -   red: Failed N tests cases, failed M assertions.
+// - green: Passed [both/all] N tests cases with M assertions.
+void printTotals(std::ostream& out, const Totals& totals) {
+    if (totals.testCases.total() == 0) {
+        out << "No tests ran.";
+    } else if (totals.testCases.failed == totals.testCases.total()) {
+        Colour colour(Colour::ResultError);
+        const std::string qualify_assertions_failed =
+            totals.assertions.failed == totals.assertions.total() ?
+            bothOrAll(totals.assertions.failed) : std::string();
+        out <<
+            "Failed " << bothOrAll(totals.testCases.failed)
+            << pluralise(totals.testCases.failed, "test case") << ", "
+            "failed " << qualify_assertions_failed <<
+            pluralise(totals.assertions.failed, "assertion") << '.';
+    } else if (totals.assertions.total() == 0) {
+        out <<
+            "Passed " << bothOrAll(totals.testCases.total())
+            << pluralise(totals.testCases.total(), "test case")
+            << " (no assertions).";
+    } else if (totals.assertions.failed) {
+        Colour colour(Colour::ResultError);
+        out <<
+            "Failed " << pluralise(totals.testCases.failed, "test case") << ", "
+            "failed " << pluralise(totals.assertions.failed, "assertion") << '.';
+    } else {
+        Colour colour(Colour::ResultSuccess);
+        out <<
+            "Passed " << bothOrAll(totals.testCases.passed)
+            << pluralise(totals.testCases.passed, "test case") <<
+            " with " << pluralise(totals.assertions.passed, "assertion") << '.';
+    }
+}
 
-        LegacyReporterRegistrar( std::string const& name ) {
-            getMutableRegistryHub().registerReporter( name, new ReporterFactory() );
+// Implementation of CompactReporter formatting
+class AssertionPrinter {
+public:
+    AssertionPrinter& operator= (AssertionPrinter const&) = delete;
+    AssertionPrinter(AssertionPrinter const&) = delete;
+    AssertionPrinter(std::ostream& _stream, AssertionStats const& _stats, bool _printInfoMessages)
+        : stream(_stream)
+        , result(_stats.assertionResult)
+        , messages(_stats.infoMessages)
+        , itMessage(_stats.infoMessages.begin())
+        , printInfoMessages(_printInfoMessages) {}
+
+    void print() {
+        printSourceInfo();
+
+        itMessage = messages.begin();
+
+        switch (result.getResultType()) {
+        case ResultWas::Ok:
+            printResultType(Colour::ResultSuccess, passedString());
+            printOriginalExpression();
+            printReconstructedExpression();
+            if (!result.hasExpression())
+                printRemainingMessages(Colour::None);
+            else
+                printRemainingMessages();
+            break;
+        case ResultWas::ExpressionFailed:
+            if (result.isOk())
+                printResultType(Colour::ResultSuccess, failedString() + std::string(" - but was ok"));
+            else
+                printResultType(Colour::Error, failedString());
+            printOriginalExpression();
+            printReconstructedExpression();
+            printRemainingMessages();
+            break;
+        case ResultWas::ThrewException:
+            printResultType(Colour::Error, failedString());
+            printIssue("unexpected exception with message:");
+            printMessage();
+            printExpressionWas();
+            printRemainingMessages();
+            break;
+        case ResultWas::FatalErrorCondition:
+            printResultType(Colour::Error, failedString());
+            printIssue("fatal error condition with message:");
+            printMessage();
+            printExpressionWas();
+            printRemainingMessages();
+            break;
+        case ResultWas::DidntThrowException:
+            printResultType(Colour::Error, failedString());
+            printIssue("expected exception, got none");
+            printExpressionWas();
+            printRemainingMessages();
+            break;
+        case ResultWas::Info:
+            printResultType(Colour::None, "info");
+            printMessage();
+            printRemainingMessages();
+            break;
+        case ResultWas::Warning:
+            printResultType(Colour::None, "warning");
+            printMessage();
+            printRemainingMessages();
+            break;
+        case ResultWas::ExplicitFailure:
+            printResultType(Colour::Error, failedString());
+            printIssue("explicitly");
+            printRemainingMessages(Colour::None);
+            break;
+            // These cases are here to prevent compiler warnings
+        case ResultWas::Unknown:
+        case ResultWas::FailureBit:
+        case ResultWas::Exception:
+            printResultType(Colour::Error, "** internal error **");
+            break;
         }
-    };
-
-    template<typename T>
-    class ReporterRegistrar {
+    }
 
-        class ReporterFactory : public SharedImpl<IReporterFactory> {
+private:
+    void printSourceInfo() const {
+        Colour colourGuard(Colour::FileName);
+        stream << result.getSourceInfo() << ':';
+    }
 
-            // *** Please Note ***:
-            // - If you end up here looking at a compiler error because it's trying to register
-            // your custom reporter class be aware that the native reporter interface has changed
-            // to IStreamingReporter. The "legacy" interface, IReporter, is still supported via
-            // an adapter. Just use REGISTER_LEGACY_REPORTER to take advantage of the adapter.
-            // However please consider updating to the new interface as the old one is now
-            // deprecated and will probably be removed quite soon!
-            // Please contact me via github if you have any questions at all about this.
-            // In fact, ideally, please contact me anyway to let me know you've hit this - as I have
-            // no idea who is actually using custom reporters at all (possibly no-one!).
-            // The new interface is designed to minimise exposure to interface changes in the future.
-            virtual IStreamingReporter* create( ReporterConfig const& config ) const {
-                return new T( config );
+    void printResultType(Colour::Code colour, std::string const& passOrFail) const {
+        if (!passOrFail.empty()) {
+            {
+                Colour colourGuard(colour);
+                stream << ' ' << passOrFail;
             }
+            stream << ':';
+        }
+    }
 
-            virtual std::string getDescription() const {
-                return T::getDescription();
+    void printIssue(std::string const& issue) const {
+        stream << ' ' << issue;
+    }
+
+    void printExpressionWas() {
+        if (result.hasExpression()) {
+            stream << ';';
+            {
+                Colour colour(dimColour());
+                stream << " expression was:";
             }
-        };
+            printOriginalExpression();
+        }
+    }
 
-    public:
+    void printOriginalExpression() const {
+        if (result.hasExpression()) {
+            stream << ' ' << result.getExpression();
+        }
+    }
 
-        ReporterRegistrar( std::string const& name ) {
-            getMutableRegistryHub().registerReporter( name, new ReporterFactory() );
+    void printReconstructedExpression() const {
+        if (result.hasExpandedExpression()) {
+            {
+                Colour colour(dimColour());
+                stream << " for: ";
+            }
+            stream << result.getExpandedExpression();
         }
-    };
+    }
 
-    template<typename T>
-    class ListenerRegistrar {
+    void printMessage() {
+        if (itMessage != messages.end()) {
+            stream << " '" << itMessage->message << '\'';
+            ++itMessage;
+        }
+    }
 
-        class ListenerFactory : public SharedImpl<IReporterFactory> {
+    void printRemainingMessages(Colour::Code colour = dimColour()) {
+        if (itMessage == messages.end())
+            return;
 
-            virtual IStreamingReporter* create( ReporterConfig const& config ) const {
-                return new T( config );
-            }
-            virtual std::string getDescription() const {
-                return std::string();
-            }
-        };
+        const auto itEnd = messages.cend();
+        const auto N = static_cast<std::size_t>(std::distance(itMessage, itEnd));
 
-    public:
+        {
+            Colour colourGuard(colour);
+            stream << " with " << pluralise(N, "message") << ':';
+        }
 
-        ListenerRegistrar() {
-            getMutableRegistryHub().registerListener( new ListenerFactory() );
+        while (itMessage != itEnd) {
+            // If this assertion is a warning ignore any INFO messages
+            if (printInfoMessages || itMessage->type != ResultWas::Info) {
+                printMessage();
+                if (itMessage != itEnd) {
+                    Colour colourGuard(dimColour());
+                    stream << " and";
+                }
+                continue;
+            }
+            ++itMessage;
         }
-    };
-}
+    }
 
-#define INTERNAL_CATCH_REGISTER_LEGACY_REPORTER( name, reporterType ) \
-    namespace{ Catch::LegacyReporterRegistrar<reporterType> catch_internal_RegistrarFor##reporterType( name ); }
+private:
+    std::ostream& stream;
+    AssertionResult const& result;
+    std::vector<MessageInfo> messages;
+    std::vector<MessageInfo>::const_iterator itMessage;
+    bool printInfoMessages;
+};
 
-#define INTERNAL_CATCH_REGISTER_REPORTER( name, reporterType ) \
-    namespace{ Catch::ReporterRegistrar<reporterType> catch_internal_RegistrarFor##reporterType( name ); }
+} // anon namespace
 
-// Deprecated - use the form without INTERNAL_
-#define INTERNAL_CATCH_REGISTER_LISTENER( listenerType ) \
-    namespace{ Catch::ListenerRegistrar<listenerType> catch_internal_RegistrarFor##listenerType; }
+        std::string CompactReporter::getDescription() {
+            return "Reports test results on a single line, suitable for IDEs";
+        }
 
-#define CATCH_REGISTER_LISTENER( listenerType ) \
-    namespace{ Catch::ListenerRegistrar<listenerType> catch_internal_RegistrarFor##listenerType; }
+        void CompactReporter::noMatchingTestCases( std::string const& spec ) {
+            stream << "No test cases matched '" << spec << '\'' << std::endl;
+        }
 
-// #included from: ../internal/catch_xmlwriter.hpp
-#define TWOBLUECUBES_CATCH_XMLWRITER_HPP_INCLUDED
+        void CompactReporter::assertionStarting( AssertionInfo const& ) {}
 
-#include <sstream>
-#include <string>
-#include <vector>
-#include <iomanip>
+        bool CompactReporter::assertionEnded( AssertionStats const& _assertionStats ) {
+            AssertionResult const& result = _assertionStats.assertionResult;
 
-namespace Catch {
+            bool printInfoMessages = true;
 
-    class XmlEncode {
-    public:
-        enum ForWhat { ForTextNodes, ForAttributes };
+            // Drop out if result was successful and we're not printing those
+            if( !m_config->includeSuccessfulResults() && result.isOk() ) {
+                if( result.getResultType() != ResultWas::Warning )
+                    return false;
+                printInfoMessages = false;
+            }
 
-        XmlEncode( std::string const& str, ForWhat forWhat = ForTextNodes )
-        :   m_str( str ),
-            m_forWhat( forWhat )
-        {}
+            AssertionPrinter printer( stream, _assertionStats, printInfoMessages );
+            printer.print();
 
-        void encodeTo( std::ostream& os ) const {
-
-            // Apostrophe escaping not necessary if we always use " to write attributes
-            // (see: http://www.w3.org/TR/xml/#syntax)
-
-            for( std::size_t i = 0; i < m_str.size(); ++ i ) {
-                char c = m_str[i];
-                switch( c ) {
-                    case '<':   os << "&lt;"; break;
-                    case '&':   os << "&amp;"; break;
-
-                    case '>':
-                        // See: http://www.w3.org/TR/xml/#syntax
-                        if( i > 2 && m_str[i-1] == ']' && m_str[i-2] == ']' )
-                            os << "&gt;";
-                        else
-                            os << c;
-                        break;
-
-                    case '\"':
-                        if( m_forWhat == ForAttributes )
-                            os << "&quot;";
-                        else
-                            os << c;
-                        break;
-
-                    default:
-                        // Escape control chars - based on contribution by @espenalb in PR #465 and
-                        // by @mrpi PR #588
-                        if ( ( c >= 0 && c < '\x09' ) || ( c > '\x0D' && c < '\x20') || c=='\x7F' ) {
-                            // see http://stackoverflow.com/questions/404107/why-are-control-characters-illegal-in-xml-1-0
-                            os << "\\x" << std::uppercase << std::hex << std::setfill('0') << std::setw(2)
-                               << static_cast<int>( c );
-                        }
-                        else
-                            os << c;
-                }
+            stream << std::endl;
+            return true;
+        }
+
+        void CompactReporter::sectionEnded(SectionStats const& _sectionStats) {
+            double dur = _sectionStats.durationInSeconds;
+            if ( shouldShowDuration( *m_config, dur ) ) {
+                stream << getFormattedDuration( dur ) << " s: " << _sectionStats.sectionInfo.name << std::endl;
             }
         }
 
-        friend std::ostream& operator << ( std::ostream& os, XmlEncode const& xmlEncode ) {
-            xmlEncode.encodeTo( os );
-            return os;
+        void CompactReporter::testRunEnded( TestRunStats const& _testRunStats ) {
+            printTotals( stream, _testRunStats.totals );
+            stream << '\n' << std::endl;
+            StreamingReporterBase::testRunEnded( _testRunStats );
         }
 
-    private:
-        std::string m_str;
-        ForWhat m_forWhat;
-    };
+        CompactReporter::~CompactReporter() {}
 
-    class XmlWriter {
-    public:
+    CATCH_REGISTER_REPORTER( "compact", CompactReporter )
 
-        class ScopedElement {
-        public:
-            ScopedElement( XmlWriter* writer )
-            :   m_writer( writer )
-            {}
+} // end namespace Catch
+// end catch_reporter_compact.cpp
+// start catch_reporter_console.cpp
 
-            ScopedElement( ScopedElement const& other )
-            :   m_writer( other.m_writer ){
-                other.m_writer = CATCH_NULL;
-            }
+#include <cfloat>
+#include <cstdio>
 
-            ~ScopedElement() {
-                if( m_writer )
-                    m_writer->endElement();
-            }
+#if defined(_MSC_VER)
+#pragma warning(push)
+#pragma warning(disable:4061) // Not all labels are EXPLICITLY handled in switch
+ // Note that 4062 (not all labels are handled and default is missing) is enabled
+#endif
 
-            ScopedElement& writeText( std::string const& text, bool indent = true ) {
-                m_writer->writeText( text, indent );
-                return *this;
-            }
+#if defined(__clang__)
+#  pragma clang diagnostic push
+// For simplicity, benchmarking-only helpers are always enabled
+#  pragma clang diagnostic ignored "-Wunused-function"
+#endif
 
-            template<typename T>
-            ScopedElement& writeAttribute( std::string const& name, T const& attribute ) {
-                m_writer->writeAttribute( name, attribute );
-                return *this;
-            }
+namespace Catch {
 
-        private:
-            mutable XmlWriter* m_writer;
-        };
+namespace {
 
-        XmlWriter()
-        :   m_tagIsOpen( false ),
-            m_needsNewline( false ),
-            m_os( Catch::cout() )
-        {
-            writeDeclaration();
+// Formatter impl for ConsoleReporter
+class ConsoleAssertionPrinter {
+public:
+    ConsoleAssertionPrinter& operator= (ConsoleAssertionPrinter const&) = delete;
+    ConsoleAssertionPrinter(ConsoleAssertionPrinter const&) = delete;
+    ConsoleAssertionPrinter(std::ostream& _stream, AssertionStats const& _stats, bool _printInfoMessages)
+        : stream(_stream),
+        stats(_stats),
+        result(_stats.assertionResult),
+        colour(Colour::None),
+        message(result.getMessage()),
+        messages(_stats.infoMessages),
+        printInfoMessages(_printInfoMessages) {
+        switch (result.getResultType()) {
+        case ResultWas::Ok:
+            colour = Colour::Success;
+            passOrFail = "PASSED";
+            //if( result.hasMessage() )
+            if (_stats.infoMessages.size() == 1)
+                messageLabel = "with message";
+            if (_stats.infoMessages.size() > 1)
+                messageLabel = "with messages";
+            break;
+        case ResultWas::ExpressionFailed:
+            if (result.isOk()) {
+                colour = Colour::Success;
+                passOrFail = "FAILED - but was ok";
+            } else {
+                colour = Colour::Error;
+                passOrFail = "FAILED";
+            }
+            if (_stats.infoMessages.size() == 1)
+                messageLabel = "with message";
+            if (_stats.infoMessages.size() > 1)
+                messageLabel = "with messages";
+            break;
+        case ResultWas::ThrewException:
+            colour = Colour::Error;
+            passOrFail = "FAILED";
+            messageLabel = "due to unexpected exception with ";
+            if (_stats.infoMessages.size() == 1)
+                messageLabel += "message";
+            if (_stats.infoMessages.size() > 1)
+                messageLabel += "messages";
+            break;
+        case ResultWas::FatalErrorCondition:
+            colour = Colour::Error;
+            passOrFail = "FAILED";
+            messageLabel = "due to a fatal error condition";
+            break;
+        case ResultWas::DidntThrowException:
+            colour = Colour::Error;
+            passOrFail = "FAILED";
+            messageLabel = "because no exception was thrown where one was expected";
+            break;
+        case ResultWas::Info:
+            messageLabel = "info";
+            break;
+        case ResultWas::Warning:
+            messageLabel = "warning";
+            break;
+        case ResultWas::ExplicitFailure:
+            passOrFail = "FAILED";
+            colour = Colour::Error;
+            if (_stats.infoMessages.size() == 1)
+                messageLabel = "explicitly with message";
+            if (_stats.infoMessages.size() > 1)
+                messageLabel = "explicitly with messages";
+            break;
+            // These cases are here to prevent compiler warnings
+        case ResultWas::Unknown:
+        case ResultWas::FailureBit:
+        case ResultWas::Exception:
+            passOrFail = "** internal error **";
+            colour = Colour::Error;
+            break;
+        }
+    }
+
+    void print() const {
+        printSourceInfo();
+        if (stats.totals.assertions.total() > 0) {
+            printResultType();
+            printOriginalExpression();
+            printReconstructedExpression();
+        } else {
+            stream << '\n';
         }
+        printMessage();
+    }
 
-        XmlWriter( std::ostream& os )
-        :   m_tagIsOpen( false ),
-            m_needsNewline( false ),
-            m_os( os )
-        {
-            writeDeclaration();
+private:
+    void printResultType() const {
+        if (!passOrFail.empty()) {
+            Colour colourGuard(colour);
+            stream << passOrFail << ":\n";
         }
-
-        ~XmlWriter() {
-            while( !m_tags.empty() )
-                endElement();
+    }
+    void printOriginalExpression() const {
+        if (result.hasExpression()) {
+            Colour colourGuard(Colour::OriginalExpression);
+            stream << "  ";
+            stream << result.getExpressionInMacro();
+            stream << '\n';
         }
-
-        XmlWriter& startElement( std::string const& name ) {
-            ensureTagClosed();
-            newlineIfNecessary();
-            m_os << m_indent << '<' << name;
-            m_tags.push_back( name );
-            m_indent += "  ";
-            m_tagIsOpen = true;
-            return *this;
+    }
+    void printReconstructedExpression() const {
+        if (result.hasExpandedExpression()) {
+            stream << "with expansion:\n";
+            Colour colourGuard(Colour::ReconstructedExpression);
+            stream << Column(result.getExpandedExpression()).indent(2) << '\n';
         }
-
-        ScopedElement scopedElement( std::string const& name ) {
-            ScopedElement scoped( this );
-            startElement( name );
-            return scoped;
+    }
+    void printMessage() const {
+        if (!messageLabel.empty())
+            stream << messageLabel << ':' << '\n';
+        for (auto const& msg : messages) {
+            // If this assertion is a warning ignore any INFO messages
+            if (printInfoMessages || msg.type != ResultWas::Info)
+                stream << Column(msg.message).indent(2) << '\n';
         }
+    }
+    void printSourceInfo() const {
+        Colour colourGuard(Colour::FileName);
+        stream << result.getSourceInfo() << ": ";
+    }
 
-        XmlWriter& endElement() {
-            newlineIfNecessary();
-            m_indent = m_indent.substr( 0, m_indent.size()-2 );
-            if( m_tagIsOpen ) {
-                m_os << "/>";
-                m_tagIsOpen = false;
-            }
-            else {
-                m_os << m_indent << "</" << m_tags.back() << ">";
-            }
-            m_os << std::endl;
-            m_tags.pop_back();
-            return *this;
-        }
+    std::ostream& stream;
+    AssertionStats const& stats;
+    AssertionResult const& result;
+    Colour::Code colour;
+    std::string passOrFail;
+    std::string messageLabel;
+    std::string message;
+    std::vector<MessageInfo> messages;
+    bool printInfoMessages;
+};
 
-        XmlWriter& writeAttribute( std::string const& name, std::string const& attribute ) {
-            if( !name.empty() && !attribute.empty() )
-                m_os << ' ' << name << "=\"" << XmlEncode( attribute, XmlEncode::ForAttributes ) << '"';
-            return *this;
+std::size_t makeRatio(std::size_t number, std::size_t total) {
+    std::size_t ratio = total > 0 ? CATCH_CONFIG_CONSOLE_WIDTH * number / total : 0;
+    return (ratio == 0 && number > 0) ? 1 : ratio;
+}
+
+std::size_t& findMax(std::size_t& i, std::size_t& j, std::size_t& k) {
+    if (i > j && i > k)
+        return i;
+    else if (j > k)
+        return j;
+    else
+        return k;
+}
+
+struct ColumnInfo {
+    enum Justification { Left, Right };
+    std::string name;
+    int width;
+    Justification justification;
+};
+struct ColumnBreak {};
+struct RowBreak {};
+
+class Duration {
+    enum class Unit {
+        Auto,
+        Nanoseconds,
+        Microseconds,
+        Milliseconds,
+        Seconds,
+        Minutes
+    };
+    static const uint64_t s_nanosecondsInAMicrosecond = 1000;
+    static const uint64_t s_nanosecondsInAMillisecond = 1000 * s_nanosecondsInAMicrosecond;
+    static const uint64_t s_nanosecondsInASecond = 1000 * s_nanosecondsInAMillisecond;
+    static const uint64_t s_nanosecondsInAMinute = 60 * s_nanosecondsInASecond;
+
+    double m_inNanoseconds;
+    Unit m_units;
+
+public:
+    explicit Duration(double inNanoseconds, Unit units = Unit::Auto)
+        : m_inNanoseconds(inNanoseconds),
+        m_units(units) {
+        if (m_units == Unit::Auto) {
+            if (m_inNanoseconds < s_nanosecondsInAMicrosecond)
+                m_units = Unit::Nanoseconds;
+            else if (m_inNanoseconds < s_nanosecondsInAMillisecond)
+                m_units = Unit::Microseconds;
+            else if (m_inNanoseconds < s_nanosecondsInASecond)
+                m_units = Unit::Milliseconds;
+            else if (m_inNanoseconds < s_nanosecondsInAMinute)
+                m_units = Unit::Seconds;
+            else
+                m_units = Unit::Minutes;
         }
 
-        XmlWriter& writeAttribute( std::string const& name, bool attribute ) {
-            m_os << ' ' << name << "=\"" << ( attribute ? "true" : "false" ) << '"';
-            return *this;
-        }
+    }
 
-        template<typename T>
-        XmlWriter& writeAttribute( std::string const& name, T const& attribute ) {
-            std::ostringstream oss;
-            oss << attribute;
-            return writeAttribute( name, oss.str() );
+    auto value() const -> double {
+        switch (m_units) {
+        case Unit::Microseconds:
+            return m_inNanoseconds / static_cast<double>(s_nanosecondsInAMicrosecond);
+        case Unit::Milliseconds:
+            return m_inNanoseconds / static_cast<double>(s_nanosecondsInAMillisecond);
+        case Unit::Seconds:
+            return m_inNanoseconds / static_cast<double>(s_nanosecondsInASecond);
+        case Unit::Minutes:
+            return m_inNanoseconds / static_cast<double>(s_nanosecondsInAMinute);
+        default:
+            return m_inNanoseconds;
         }
-
-        XmlWriter& writeText( std::string const& text, bool indent = true ) {
-            if( !text.empty() ){
-                bool tagWasOpen = m_tagIsOpen;
-                ensureTagClosed();
-                if( tagWasOpen && indent )
-                    m_os << m_indent;
-                m_os << XmlEncode( text );
-                m_needsNewline = true;
-            }
-            return *this;
+    }
+    auto unitsAsString() const -> std::string {
+        switch (m_units) {
+        case Unit::Nanoseconds:
+            return "ns";
+        case Unit::Microseconds:
+            return "us";
+        case Unit::Milliseconds:
+            return "ms";
+        case Unit::Seconds:
+            return "s";
+        case Unit::Minutes:
+            return "m";
+        default:
+            return "** internal error **";
         }
 
-        XmlWriter& writeComment( std::string const& text ) {
-            ensureTagClosed();
-            m_os << m_indent << "<!--" << text << "-->";
-            m_needsNewline = true;
-            return *this;
-        }
+    }
+    friend auto operator << (std::ostream& os, Duration const& duration) -> std::ostream& {
+        return os << duration.value() << ' ' << duration.unitsAsString();
+    }
+};
+} // end anon namespace
 
-        void writeStylesheetRef( std::string const& url ) {
-            m_os << "<?xml-stylesheet type=\"text/xsl\" href=\"" << url << "\"?>\n";
-        }
+class TablePrinter {
+    std::ostream& m_os;
+    std::vector<ColumnInfo> m_columnInfos;
+    std::ostringstream m_oss;
+    int m_currentColumn = -1;
+    bool m_isOpen = false;
 
-        XmlWriter& writeBlankLine() {
-            ensureTagClosed();
-            m_os << '\n';
-            return *this;
-        }
+public:
+    TablePrinter( std::ostream& os, std::vector<ColumnInfo> columnInfos )
+    :   m_os( os ),
+        m_columnInfos( std::move( columnInfos ) ) {}
 
-        void ensureTagClosed() {
-            if( m_tagIsOpen ) {
-                m_os << ">" << std::endl;
-                m_tagIsOpen = false;
-            }
-        }
+    auto columnInfos() const -> std::vector<ColumnInfo> const& {
+        return m_columnInfos;
+    }
 
-    private:
-        XmlWriter( XmlWriter const& );
-        void operator=( XmlWriter const& );
+    void open() {
+        if (!m_isOpen) {
+            m_isOpen = true;
+            *this << RowBreak();
 
-        void writeDeclaration() {
-            m_os << "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\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';
 
-        void newlineIfNecessary() {
-            if( m_needsNewline ) {
-                m_os << std::endl;
-                m_needsNewline = false;
-            }
+            m_os << Catch::getLineOfChars<'-'>() << '\n';
         }
+    }
+    void close() {
+        if (m_isOpen) {
+            *this << RowBreak();
+            m_os << std::endl;
+            m_isOpen = false;
+        }
+    }
 
-        bool m_tagIsOpen;
-        bool m_needsNewline;
-        std::vector<std::string> m_tags;
-        std::string m_indent;
-        std::ostream& m_os;
-    };
+    template<typename T>
+    friend TablePrinter& operator << (TablePrinter& tp, T const& value) {
+        tp.m_oss << value;
+        return tp;
+    }
+
+    friend TablePrinter& operator << (TablePrinter& tp, ColumnBreak) {
+        auto colStr = tp.m_oss.str();
+        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_currentColumn++;
+
+        auto colInfo = tp.m_columnInfos[tp.m_currentColumn];
+        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 << ' ';
+        else
+            tp.m_os << padding << colStr << ' ';
+        return tp;
+    }
 
-}
+    friend TablePrinter& operator << (TablePrinter& tp, RowBreak) {
+        if (tp.m_currentColumn > 0) {
+            tp.m_os << '\n';
+            tp.m_currentColumn = -1;
+        }
+        return tp;
+    }
+};
 
-namespace Catch {
-    class XmlReporter : public StreamingReporterBase {
-    public:
-        XmlReporter( ReporterConfig const& _config )
-        :   StreamingReporterBase( _config ),
-            m_xml(_config.stream()),
-            m_sectionDepth( 0 )
+ConsoleReporter::ConsoleReporter(ReporterConfig const& config)
+    : StreamingReporterBase(config),
+    m_tablePrinter(new TablePrinter(config.stream(),
+        [&config]() -> std::vector<ColumnInfo> {
+        if (config.fullConfig()->benchmarkNoAnalysis())
         {
-            m_reporterPrefs.shouldRedirectStdOut = true;
+            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;
 
-        virtual ~XmlReporter() CATCH_OVERRIDE;
+std::string ConsoleReporter::getDescription() {
+    return "Reports test results as plain lines of text";
+}
 
-        static std::string getDescription() {
-            return "Reports test results as an XML document";
-        }
+void ConsoleReporter::noMatchingTestCases(std::string const& spec) {
+    stream << "No test cases matched '" << spec << '\'' << std::endl;
+}
 
-        virtual std::string getStylesheetRef() const {
-            return std::string();
-        }
+void ConsoleReporter::reportInvalidArguments(std::string const&arg){
+    stream << "Invalid Filter: " << arg << std::endl;
+}
 
-        void writeSourceInfo( SourceLineInfo const& sourceInfo ) {
-            m_xml
-                .writeAttribute( "filename", sourceInfo.file )
-                .writeAttribute( "line", sourceInfo.line );
-        }
+void ConsoleReporter::assertionStarting(AssertionInfo const&) {}
 
-    public: // StreamingReporterBase
+bool ConsoleReporter::assertionEnded(AssertionStats const& _assertionStats) {
+    AssertionResult const& result = _assertionStats.assertionResult;
 
-        virtual void noMatchingTestCases( std::string const& s ) CATCH_OVERRIDE {
-            StreamingReporterBase::noMatchingTestCases( s );
-        }
+    bool includeResults = m_config->includeSuccessfulResults() || !result.isOk();
 
-        virtual void testRunStarting( TestRunInfo const& testInfo ) CATCH_OVERRIDE {
-            StreamingReporterBase::testRunStarting( testInfo );
-            std::string stylesheetRef = getStylesheetRef();
-            if( !stylesheetRef.empty() )
-                m_xml.writeStylesheetRef( stylesheetRef );
-            m_xml.startElement( "Catch" );
-            if( !m_config->name().empty() )
-                m_xml.writeAttribute( "name", m_config->name() );
-        }
+    // Drop out if result was successful but we're not printing them.
+    if (!includeResults && result.getResultType() != ResultWas::Warning)
+        return false;
 
-        virtual void testGroupStarting( GroupInfo const& groupInfo ) CATCH_OVERRIDE {
-            StreamingReporterBase::testGroupStarting( groupInfo );
-            m_xml.startElement( "Group" )
-                .writeAttribute( "name", groupInfo.name );
-        }
+    lazyPrint();
+
+    ConsoleAssertionPrinter printer(stream, _assertionStats, includeResults);
+    printer.print();
+    stream << std::endl;
+    return true;
+}
 
-        virtual void testCaseStarting( TestCaseInfo const& testInfo ) CATCH_OVERRIDE {
-            StreamingReporterBase::testCaseStarting(testInfo);
-            m_xml.startElement( "TestCase" )
-                .writeAttribute( "name", trim( testInfo.name ) )
-                .writeAttribute( "description", testInfo.description )
-                .writeAttribute( "tags", testInfo.tagsAsString );
+void ConsoleReporter::sectionStarting(SectionInfo const& _sectionInfo) {
+    m_tablePrinter->close();
+    m_headerPrinted = false;
+    StreamingReporterBase::sectionStarting(_sectionInfo);
+}
+void ConsoleReporter::sectionEnded(SectionStats const& _sectionStats) {
+    m_tablePrinter->close();
+    if (_sectionStats.missingAssertions) {
+        lazyPrint();
+        Colour colour(Colour::ResultError);
+        if (m_sectionStack.size() > 1)
+            stream << "\nNo assertions in section";
+        else
+            stream << "\nNo assertions in test case";
+        stream << " '" << _sectionStats.sectionInfo.name << "'\n" << 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;
+    }
+    StreamingReporterBase::sectionEnded(_sectionStats);
+}
 
-            writeSourceInfo( testInfo.lineInfo );
+#if defined(CATCH_CONFIG_ENABLE_BENCHMARKING)
+void ConsoleReporter::benchmarkPreparing(std::string const& name) {
+	lazyPrintWithoutClosingBenchmarkTable();
 
-            if ( m_config->showDurations() == ShowDurations::Always )
-                m_testCaseTimer.start();
-            m_xml.ensureTagClosed();
-        }
+	auto nameCol = Column(name).width(static_cast<std::size_t>(m_tablePrinter->columnInfos()[0].width - 2));
 
-        virtual void sectionStarting( SectionInfo const& sectionInfo ) CATCH_OVERRIDE {
-            StreamingReporterBase::sectionStarting( sectionInfo );
-            if( m_sectionDepth++ > 0 ) {
-                m_xml.startElement( "Section" )
-                    .writeAttribute( "name", trim( sectionInfo.name ) )
-                    .writeAttribute( "description", sectionInfo.description );
-                writeSourceInfo( sectionInfo.lineInfo );
-                m_xml.ensureTagClosed();
-            }
-        }
+	bool firstLine = true;
+	for (auto line : nameCol) {
+		if (!firstLine)
+			(*m_tablePrinter) << ColumnBreak() << ColumnBreak() << ColumnBreak();
+		else
+			firstLine = false;
 
-        virtual void assertionStarting( AssertionInfo const& ) CATCH_OVERRIDE { }
+		(*m_tablePrinter) << line << ColumnBreak();
+	}
+}
 
-        virtual bool assertionEnded( AssertionStats const& assertionStats ) CATCH_OVERRIDE {
+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();
+    }
+}
 
-            AssertionResult const& result = assertionStats.assertionResult;
+void ConsoleReporter::benchmarkFailed(std::string const& error) {
+	Colour colour(Colour::Red);
+    (*m_tablePrinter)
+        << "Benchmark failed (" << error << ')'
+        << ColumnBreak() << RowBreak();
+}
+#endif // CATCH_CONFIG_ENABLE_BENCHMARKING
 
-            bool includeResults = m_config->includeSuccessfulResults() || !result.isOk();
+void ConsoleReporter::testCaseEnded(TestCaseStats const& _testCaseStats) {
+    m_tablePrinter->close();
+    StreamingReporterBase::testCaseEnded(_testCaseStats);
+    m_headerPrinted = false;
+}
+void ConsoleReporter::testGroupEnded(TestGroupStats const& _testGroupStats) {
+    if (currentGroupInfo.used) {
+        printSummaryDivider();
+        stream << "Summary for group '" << _testGroupStats.groupInfo.name << "':\n";
+        printTotals(_testGroupStats.totals);
+        stream << '\n' << std::endl;
+    }
+    StreamingReporterBase::testGroupEnded(_testGroupStats);
+}
+void ConsoleReporter::testRunEnded(TestRunStats const& _testRunStats) {
+    printTotalsDivider(_testRunStats.totals);
+    printTotals(_testRunStats.totals);
+    stream << std::endl;
+    StreamingReporterBase::testRunEnded(_testRunStats);
+}
+void ConsoleReporter::testRunStarting(TestRunInfo const& _testInfo) {
+    StreamingReporterBase::testRunStarting(_testInfo);
+    printTestFilters();
+}
 
-            if( includeResults || result.getResultType() == ResultWas::Warning ) {
-                // Print any info messages in <Info> tags.
-                for( std::vector<MessageInfo>::const_iterator it = assertionStats.infoMessages.begin(), itEnd = assertionStats.infoMessages.end();
-                     it != itEnd;
-                     ++it ) {
-                    if( it->type == ResultWas::Info && includeResults ) {
-                        m_xml.scopedElement( "Info" )
-                                .writeText( it->message );
-                    } else if ( it->type == ResultWas::Warning ) {
-                        m_xml.scopedElement( "Warning" )
-                                .writeText( it->message );
-                    }
-                }
-            }
+void ConsoleReporter::lazyPrint() {
 
-            // Drop out if result was successful but we're not printing them.
-            if( !includeResults && result.getResultType() != ResultWas::Warning )
-                return true;
+    m_tablePrinter->close();
+    lazyPrintWithoutClosingBenchmarkTable();
+}
 
-            // Print the expression if there is one.
-            if( result.hasExpression() ) {
-                m_xml.startElement( "Expression" )
-                    .writeAttribute( "success", result.succeeded() )
-                    .writeAttribute( "type", result.getTestMacroName() );
+void ConsoleReporter::lazyPrintWithoutClosingBenchmarkTable() {
 
-                writeSourceInfo( result.getSourceInfo() );
+    if (!currentTestRunInfo.used)
+        lazyPrintRunInfo();
+    if (!currentGroupInfo.used)
+        lazyPrintGroupInfo();
 
-                m_xml.scopedElement( "Original" )
-                    .writeText( result.getExpression() );
-                m_xml.scopedElement( "Expanded" )
-                    .writeText( result.getExpandedExpression() );
-            }
+    if (!m_headerPrinted) {
+        printTestCaseAndSectionHeader();
+        m_headerPrinted = true;
+    }
+}
+void ConsoleReporter::lazyPrintRunInfo() {
+    stream << '\n' << getLineOfChars<'~'>() << '\n';
+    Colour colour(Colour::SecondaryText);
+    stream << currentTestRunInfo->name
+        << " is a Catch v" << libraryVersion() << " host application.\n"
+        << "Run with -? for options\n\n";
 
-            // And... Print a result applicable to each result type.
-            switch( result.getResultType() ) {
-                case ResultWas::ThrewException:
-                    m_xml.startElement( "Exception" );
-                    writeSourceInfo( result.getSourceInfo() );
-                    m_xml.writeText( result.getMessage() );
-                    m_xml.endElement();
-                    break;
-                case ResultWas::FatalErrorCondition:
-                    m_xml.startElement( "FatalErrorCondition" );
-                    writeSourceInfo( result.getSourceInfo() );
-                    m_xml.writeText( result.getMessage() );
-                    m_xml.endElement();
-                    break;
-                case ResultWas::Info:
-                    m_xml.scopedElement( "Info" )
-                        .writeText( result.getMessage() );
-                    break;
-                case ResultWas::Warning:
-                    // Warning will already have been written
-                    break;
-                case ResultWas::ExplicitFailure:
-                    m_xml.startElement( "Failure" );
-                    writeSourceInfo( result.getSourceInfo() );
-                    m_xml.writeText( result.getMessage() );
-                    m_xml.endElement();
-                    break;
-                default:
-                    break;
-            }
+    if (m_config->rngSeed() != 0)
+        stream << "Randomness seeded to: " << m_config->rngSeed() << "\n\n";
 
-            if( result.hasExpression() )
-                m_xml.endElement();
+    currentTestRunInfo.used = true;
+}
+void ConsoleReporter::lazyPrintGroupInfo() {
+    if (!currentGroupInfo->name.empty() && currentGroupInfo->groupsCounts > 1) {
+        printClosedHeader("Group: " + currentGroupInfo->name);
+        currentGroupInfo.used = true;
+    }
+}
+void ConsoleReporter::printTestCaseAndSectionHeader() {
+    assert(!m_sectionStack.empty());
+    printOpenHeader(currentTestCaseInfo->name);
 
-            return true;
-        }
+    if (m_sectionStack.size() > 1) {
+        Colour colourGuard(Colour::Headers);
 
-        virtual void sectionEnded( SectionStats const& sectionStats ) CATCH_OVERRIDE {
-            StreamingReporterBase::sectionEnded( sectionStats );
-            if( --m_sectionDepth > 0 ) {
-                XmlWriter::ScopedElement e = m_xml.scopedElement( "OverallResults" );
-                e.writeAttribute( "successes", sectionStats.assertions.passed );
-                e.writeAttribute( "failures", sectionStats.assertions.failed );
-                e.writeAttribute( "expectedFailures", sectionStats.assertions.failedButOk );
+        auto
+            it = m_sectionStack.begin() + 1, // Skip first section (test case)
+            itEnd = m_sectionStack.end();
+        for (; it != itEnd; ++it)
+            printHeaderString(it->name, 2);
+    }
 
-                if ( m_config->showDurations() == ShowDurations::Always )
-                    e.writeAttribute( "durationInSeconds", sectionStats.durationInSeconds );
+    SourceLineInfo lineInfo = m_sectionStack.back().lineInfo;
 
-                m_xml.endElement();
-            }
-        }
+    stream << getLineOfChars<'-'>() << '\n';
+    Colour colourGuard(Colour::FileName);
+    stream << lineInfo << '\n';
+    stream << getLineOfChars<'.'>() << '\n' << std::endl;
+}
 
-        virtual void testCaseEnded( TestCaseStats const& testCaseStats ) CATCH_OVERRIDE {
-            StreamingReporterBase::testCaseEnded( testCaseStats );
-            XmlWriter::ScopedElement e = m_xml.scopedElement( "OverallResult" );
-            e.writeAttribute( "success", testCaseStats.totals.assertions.allOk() );
+void ConsoleReporter::printClosedHeader(std::string const& _name) {
+    printOpenHeader(_name);
+    stream << getLineOfChars<'.'>() << '\n';
+}
+void ConsoleReporter::printOpenHeader(std::string const& _name) {
+    stream << getLineOfChars<'-'>() << '\n';
+    {
+        Colour colourGuard(Colour::Headers);
+        printHeaderString(_name);
+    }
+}
 
-            if ( m_config->showDurations() == ShowDurations::Always )
-                e.writeAttribute( "durationInSeconds", m_testCaseTimer.getElapsedSeconds() );
+// if string has a : in first line will set indent to follow it on
+// subsequent lines
+void ConsoleReporter::printHeaderString(std::string const& _string, std::size_t indent) {
+    std::size_t i = _string.find(": ");
+    if (i != std::string::npos)
+        i += 2;
+    else
+        i = 0;
+    stream << Column(_string).indent(indent + i).initialIndent(indent) << '\n';
+}
 
-            if( !testCaseStats.stdOut.empty() )
-                m_xml.scopedElement( "StdOut" ).writeText( trim( testCaseStats.stdOut ), false );
-            if( !testCaseStats.stdErr.empty() )
-                m_xml.scopedElement( "StdErr" ).writeText( trim( testCaseStats.stdErr ), false );
+struct SummaryColumn {
+
+    SummaryColumn( std::string _label, Colour::Code _colour )
+    :   label( std::move( _label ) ),
+        colour( _colour ) {}
+    SummaryColumn addRow( std::size_t count ) {
+        ReusableStringStream rss;
+        rss << count;
+        std::string row = rss.str();
+        for (auto& oldRow : rows) {
+            while (oldRow.size() < row.size())
+                oldRow = ' ' + oldRow;
+            while (oldRow.size() > row.size())
+                row = ' ' + row;
+        }
+        rows.push_back(row);
+        return *this;
+    }
 
-            m_xml.endElement();
-        }
+    std::string label;
+    Colour::Code colour;
+    std::vector<std::string> rows;
 
-        virtual void testGroupEnded( TestGroupStats const& testGroupStats ) CATCH_OVERRIDE {
-            StreamingReporterBase::testGroupEnded( testGroupStats );
-            // TODO: Check testGroupStats.aborting and act accordingly.
-            m_xml.scopedElement( "OverallResults" )
-                .writeAttribute( "successes", testGroupStats.totals.assertions.passed )
-                .writeAttribute( "failures", testGroupStats.totals.assertions.failed )
-                .writeAttribute( "expectedFailures", testGroupStats.totals.assertions.failedButOk );
-            m_xml.endElement();
-        }
+};
 
-        virtual void testRunEnded( TestRunStats const& testRunStats ) CATCH_OVERRIDE {
-            StreamingReporterBase::testRunEnded( testRunStats );
-            m_xml.scopedElement( "OverallResults" )
-                .writeAttribute( "successes", testRunStats.totals.assertions.passed )
-                .writeAttribute( "failures", testRunStats.totals.assertions.failed )
-                .writeAttribute( "expectedFailures", testRunStats.totals.assertions.failedButOk );
-            m_xml.endElement();
+void ConsoleReporter::printTotals( Totals const& totals ) {
+    if (totals.testCases.total() == 0) {
+        stream << Colour(Colour::Warning) << "No tests ran\n";
+    } else if (totals.assertions.total() > 0 && totals.testCases.allPassed()) {
+        stream << Colour(Colour::ResultSuccess) << "All tests passed";
+        stream << " ("
+            << pluralise(totals.assertions.passed, "assertion") << " in "
+            << pluralise(totals.testCases.passed, "test case") << ')'
+            << '\n';
+    } else {
+
+        std::vector<SummaryColumn> columns;
+        columns.push_back(SummaryColumn("", Colour::None)
+                          .addRow(totals.testCases.total())
+                          .addRow(totals.assertions.total()));
+        columns.push_back(SummaryColumn("passed", Colour::Success)
+                          .addRow(totals.testCases.passed)
+                          .addRow(totals.assertions.passed));
+        columns.push_back(SummaryColumn("failed", Colour::ResultError)
+                          .addRow(totals.testCases.failed)
+                          .addRow(totals.assertions.failed));
+        columns.push_back(SummaryColumn("failed as expected", Colour::ResultExpectedFailure)
+                          .addRow(totals.testCases.failedButOk)
+                          .addRow(totals.assertions.failedButOk));
+
+        printSummaryRow("test cases", columns, 0);
+        printSummaryRow("assertions", columns, 1);
+    }
+}
+void ConsoleReporter::printSummaryRow(std::string const& label, std::vector<SummaryColumn> const& cols, std::size_t row) {
+    for (auto col : cols) {
+        std::string value = col.rows[row];
+        if (col.label.empty()) {
+            stream << label << ": ";
+            if (value != "0")
+                stream << value;
+            else
+                stream << Colour(Colour::Warning) << "- none -";
+        } else if (value != "0") {
+            stream << Colour(Colour::LightGrey) << " | ";
+            stream << Colour(col.colour)
+                << value << ' ' << col.label;
         }
+    }
+    stream << '\n';
+}
 
-    private:
-        Timer m_testCaseTimer;
-        XmlWriter m_xml;
-        int m_sectionDepth;
-    };
+void ConsoleReporter::printTotalsDivider(Totals const& totals) {
+    if (totals.testCases.total() > 0) {
+        std::size_t failedRatio = makeRatio(totals.testCases.failed, totals.testCases.total());
+        std::size_t failedButOkRatio = makeRatio(totals.testCases.failedButOk, totals.testCases.total());
+        std::size_t passedRatio = makeRatio(totals.testCases.passed, totals.testCases.total());
+        while (failedRatio + failedButOkRatio + passedRatio < CATCH_CONFIG_CONSOLE_WIDTH - 1)
+            findMax(failedRatio, failedButOkRatio, passedRatio)++;
+        while (failedRatio + failedButOkRatio + passedRatio > CATCH_CONFIG_CONSOLE_WIDTH - 1)
+            findMax(failedRatio, failedButOkRatio, passedRatio)--;
+
+        stream << Colour(Colour::Error) << std::string(failedRatio, '=');
+        stream << Colour(Colour::ResultExpectedFailure) << std::string(failedButOkRatio, '=');
+        if (totals.testCases.allPassed())
+            stream << Colour(Colour::ResultSuccess) << std::string(passedRatio, '=');
+        else
+            stream << Colour(Colour::Success) << std::string(passedRatio, '=');
+    } else {
+        stream << Colour(Colour::Warning) << std::string(CATCH_CONFIG_CONSOLE_WIDTH - 1, '=');
+    }
+    stream << '\n';
+}
+void ConsoleReporter::printSummaryDivider() {
+    stream << getLineOfChars<'-'>() << '\n';
+}
+
+void ConsoleReporter::printTestFilters() {
+    if (m_config->testSpec().hasFilters()) {
+        Colour guard(Colour::BrightYellow);
+        stream << "Filters: " << serializeFilters(m_config->getTestsOrTags()) << '\n';
+    }
+}
 
-     INTERNAL_CATCH_REGISTER_REPORTER( "xml", XmlReporter )
+CATCH_REGISTER_REPORTER("console", ConsoleReporter)
 
 } // end namespace Catch
 
-// #included from: ../reporters/catch_reporter_junit.hpp
-#define TWOBLUECUBES_CATCH_REPORTER_JUNIT_HPP_INCLUDED
+#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
 
-#include <assert.h>
+#include <cassert>
+#include <sstream>
+#include <ctime>
+#include <algorithm>
+#include <iomanip>
 
 namespace Catch {
 
@@ -10433,7 +16812,7 @@ namespace Catch {
             // Also, UTC only, again because of backward compatibility (%z is C++11)
             time_t rawtime;
             std::time(&rawtime);
-            const size_t timeStampSize = sizeof("2017-01-16T17:06:45Z");
+            auto const timeStampSize = sizeof("2017-01-16T17:06:45Z");
 
 #ifdef _MSC_VER
             std::tm timeInfo = {};
@@ -10451,1027 +16830,717 @@ namespace Catch {
 #else
             std::strftime(timeStamp, timeStampSize, fmt, timeInfo);
 #endif
-            return std::string(timeStamp);
-        }
-
-    }
-
-    class JunitReporter : public CumulativeReporterBase {
-    public:
-        JunitReporter( ReporterConfig const& _config )
-        :   CumulativeReporterBase( _config ),
-            xml( _config.stream() ),
-            unexpectedExceptions( 0 ),
-            m_okToFail( false )
-        {
-            m_reporterPrefs.shouldRedirectStdOut = true;
-        }
-
-        virtual ~JunitReporter() CATCH_OVERRIDE;
-
-        static std::string getDescription() {
-            return "Reports test results in an XML format that looks like Ant's junitreport target";
+            return std::string(timeStamp, timeStampSize-1);
         }
 
-        virtual void noMatchingTestCases( std::string const& /*spec*/ ) CATCH_OVERRIDE {}
-
-        virtual void testRunStarting( TestRunInfo const& runInfo ) CATCH_OVERRIDE {
-            CumulativeReporterBase::testRunStarting( runInfo );
-            xml.startElement( "testsuites" );
-        }
-
-        virtual void testGroupStarting( GroupInfo const& groupInfo ) CATCH_OVERRIDE {
-            suiteTimer.start();
-            stdOutForSuite.str("");
-            stdErrForSuite.str("");
-            unexpectedExceptions = 0;
-            CumulativeReporterBase::testGroupStarting( groupInfo );
+        std::string fileNameTag(const std::vector<std::string> &tags) {
+            auto it = std::find_if(begin(tags),
+                                   end(tags),
+                                   [] (std::string const& tag) {return tag.front() == '#'; });
+            if (it != tags.end())
+                return it->substr(1);
+            return std::string();
         }
 
-        virtual void testCaseStarting( TestCaseInfo const& testCaseInfo ) CATCH_OVERRIDE {
-            m_okToFail = testCaseInfo.okToFail();
-        }
-        virtual bool assertionEnded( AssertionStats const& assertionStats ) CATCH_OVERRIDE {
-            if( assertionStats.assertionResult.getResultType() == ResultWas::ThrewException && !m_okToFail )
-                unexpectedExceptions++;
-            return CumulativeReporterBase::assertionEnded( assertionStats );
+        // 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();
         }
 
-        virtual void testCaseEnded( TestCaseStats const& testCaseStats ) CATCH_OVERRIDE {
-            stdOutForSuite << testCaseStats.stdOut;
-            stdErrForSuite << testCaseStats.stdErr;
-            CumulativeReporterBase::testCaseEnded( testCaseStats );
-        }
+    } // anonymous namespace
 
-        virtual void testGroupEnded( TestGroupStats const& testGroupStats ) CATCH_OVERRIDE {
-            double suiteTime = suiteTimer.getElapsedSeconds();
-            CumulativeReporterBase::testGroupEnded( testGroupStats );
-            writeGroup( *m_testGroups.back(), suiteTime );
+    JunitReporter::JunitReporter( ReporterConfig const& _config )
+        :   CumulativeReporterBase( _config ),
+            xml( _config.stream() )
+        {
+            m_reporterPrefs.shouldRedirectStdOut = true;
+            m_reporterPrefs.shouldReportAllAssertions = true;
         }
 
-        virtual void testRunEndedCumulative() CATCH_OVERRIDE {
-            xml.endElement();
-        }
+    JunitReporter::~JunitReporter() {}
 
-        void 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 );
-            xml.writeAttribute( "failures", stats.totals.assertions.failed-unexpectedExceptions );
-            xml.writeAttribute( "tests", stats.totals.assertions.total() );
-            xml.writeAttribute( "hostname", "tbd" ); // !TBD
-            if( m_config->showDurations() == ShowDurations::Never )
-                xml.writeAttribute( "time", "" );
-            else
-                xml.writeAttribute( "time", suiteTime );
-            xml.writeAttribute( "timestamp", getCurrentTimestamp() );
+    std::string JunitReporter::getDescription() {
+        return "Reports test results in an XML format that looks like Ant's junitreport target";
+    }
 
-            // Write test cases
-            for( TestGroupNode::ChildNodes::const_iterator
-                    it = groupNode.children.begin(), itEnd = groupNode.children.end();
-                    it != itEnd;
-                    ++it )
-                writeTestCase( **it );
+    void JunitReporter::noMatchingTestCases( std::string const& /*spec*/ ) {}
 
-            xml.scopedElement( "system-out" ).writeText( trim( stdOutForSuite.str() ), false );
-            xml.scopedElement( "system-err" ).writeText( trim( stdErrForSuite.str() ), false );
-        }
+    void JunitReporter::testRunStarting( TestRunInfo const& runInfo )  {
+        CumulativeReporterBase::testRunStarting( runInfo );
+        xml.startElement( "testsuites" );
+    }
 
-        void writeTestCase( TestCaseNode const& testCaseNode ) {
-            TestCaseStats const& stats = testCaseNode.value;
+    void JunitReporter::testGroupStarting( GroupInfo const& groupInfo ) {
+        suiteTimer.start();
+        stdOutForSuite.clear();
+        stdErrForSuite.clear();
+        unexpectedExceptions = 0;
+        CumulativeReporterBase::testGroupStarting( groupInfo );
+    }
 
-            // All test cases have exactly one section - which represents the
-            // test case itself. That section may have 0-n nested sections
-            assert( testCaseNode.children.size() == 1 );
-            SectionNode const& rootSection = *testCaseNode.children.front();
+    void JunitReporter::testCaseStarting( TestCaseInfo const& testCaseInfo ) {
+        m_okToFail = testCaseInfo.okToFail();
+    }
 
-            std::string className = stats.testInfo.className;
+    bool JunitReporter::assertionEnded( AssertionStats const& assertionStats ) {
+        if( assertionStats.assertionResult.getResultType() == ResultWas::ThrewException && !m_okToFail )
+            unexpectedExceptions++;
+        return CumulativeReporterBase::assertionEnded( assertionStats );
+    }
 
-            if( className.empty() ) {
-                if( rootSection.childSections.empty() )
-                    className = "global";
-            }
-            writeSection( className, "", rootSection );
-        }
-
-        void writeSection(  std::string const& className,
-                            std::string const& rootName,
-                            SectionNode const& sectionNode ) {
-            std::string name = trim( sectionNode.stats.sectionInfo.name );
-            if( !rootName.empty() )
-                name = rootName + '/' + name;
-
-            if( !sectionNode.assertions.empty() ||
-                !sectionNode.stdOut.empty() ||
-                !sectionNode.stdErr.empty() ) {
-                XmlWriter::ScopedElement e = xml.scopedElement( "testcase" );
-                if( className.empty() ) {
-                    xml.writeAttribute( "classname", name );
-                    xml.writeAttribute( "name", "root" );
-                }
-                else {
-                    xml.writeAttribute( "classname", className );
-                    xml.writeAttribute( "name", name );
-                }
-                xml.writeAttribute( "time", Catch::toString( sectionNode.stats.durationInSeconds ) );
-
-                writeAssertions( sectionNode );
-
-                if( !sectionNode.stdOut.empty() )
-                    xml.scopedElement( "system-out" ).writeText( trim( sectionNode.stdOut ), false );
-                if( !sectionNode.stdErr.empty() )
-                    xml.scopedElement( "system-err" ).writeText( trim( sectionNode.stdErr ), false );
-            }
-            for( SectionNode::ChildSections::const_iterator
-                    it = sectionNode.childSections.begin(),
-                    itEnd = sectionNode.childSections.end();
-                    it != itEnd;
-                    ++it )
-                if( className.empty() )
-                    writeSection( name, "", **it );
-                else
-                    writeSection( className, name, **it );
-        }
-
-        void writeAssertions( SectionNode const& sectionNode ) {
-            for( SectionNode::Assertions::const_iterator
-                    it = sectionNode.assertions.begin(), itEnd = sectionNode.assertions.end();
-                    it != itEnd;
-                    ++it )
-                writeAssertion( *it );
-        }
-        void writeAssertion( AssertionStats const& stats ) {
-            AssertionResult const& result = stats.assertionResult;
-            if( !result.isOk() ) {
-                std::string elementName;
-                switch( result.getResultType() ) {
-                    case ResultWas::ThrewException:
-                    case ResultWas::FatalErrorCondition:
-                        elementName = "error";
-                        break;
-                    case ResultWas::ExplicitFailure:
-                        elementName = "failure";
-                        break;
-                    case ResultWas::ExpressionFailed:
-                        elementName = "failure";
-                        break;
-                    case ResultWas::DidntThrowException:
-                        elementName = "failure";
-                        break;
-
-                    // We should never see these here:
-                    case ResultWas::Info:
-                    case ResultWas::Warning:
-                    case ResultWas::Ok:
-                    case ResultWas::Unknown:
-                    case ResultWas::FailureBit:
-                    case ResultWas::Exception:
-                        elementName = "internalError";
-                        break;
-                }
+    void JunitReporter::testCaseEnded( TestCaseStats const& testCaseStats ) {
+        stdOutForSuite += testCaseStats.stdOut;
+        stdErrForSuite += testCaseStats.stdErr;
+        CumulativeReporterBase::testCaseEnded( testCaseStats );
+    }
 
-                XmlWriter::ScopedElement e = xml.scopedElement( elementName );
+    void JunitReporter::testGroupEnded( TestGroupStats const& testGroupStats ) {
+        double suiteTime = suiteTimer.getElapsedSeconds();
+        CumulativeReporterBase::testGroupEnded( testGroupStats );
+        writeGroup( *m_testGroups.back(), suiteTime );
+    }
 
-                xml.writeAttribute( "message", result.getExpandedExpression() );
-                xml.writeAttribute( "type", result.getTestMacroName() );
+    void JunitReporter::testRunEndedCumulative() {
+        xml.endElement();
+    }
 
-                std::ostringstream oss;
-                if( !result.getMessage().empty() )
-                    oss << result.getMessage() << '\n';
-                for( std::vector<MessageInfo>::const_iterator
-                        it = stats.infoMessages.begin(),
-                        itEnd = stats.infoMessages.end();
-                            it != itEnd;
-                            ++it )
-                    if( it->type == ResultWas::Info )
-                        oss << it->message << '\n';
+    void JunitReporter::writeGroup( TestGroupNode const& groupNode, double suiteTime ) {
+        XmlWriter::ScopedElement e = xml.scopedElement( "testsuite" );
 
-                oss << "at " << result.getSourceInfo();
-                xml.writeText( oss.str(), false );
+        TestGroupStats const& stats = groupNode.value;
+        xml.writeAttribute( "name", stats.groupInfo.name );
+        xml.writeAttribute( "errors", unexpectedExceptions );
+        xml.writeAttribute( "failures", stats.totals.assertions.failed-unexpectedExceptions );
+        xml.writeAttribute( "tests", stats.totals.assertions.total() );
+        xml.writeAttribute( "hostname", "tbd" ); // !TBD
+        if( m_config->showDurations() == ShowDurations::Never )
+            xml.writeAttribute( "time", "" );
+        else
+            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());
             }
         }
 
-        XmlWriter xml;
-        Timer suiteTimer;
-        std::ostringstream stdOutForSuite;
-        std::ostringstream stdErrForSuite;
-        unsigned int unexpectedExceptions;
-        bool m_okToFail;
-    };
-
-    INTERNAL_CATCH_REGISTER_REPORTER( "junit", JunitReporter )
+        // Write test cases
+        for( auto const& child : groupNode.children )
+            writeTestCase( *child );
 
-} // end namespace Catch
-
-// #included from: ../reporters/catch_reporter_console.hpp
-#define TWOBLUECUBES_CATCH_REPORTER_CONSOLE_HPP_INCLUDED
-
-#include <cassert>
-#include <cfloat>
-#include <cstdio>
-
-namespace Catch {
+        xml.scopedElement( "system-out" ).writeText( trim( stdOutForSuite ), XmlFormatting::Newline );
+        xml.scopedElement( "system-err" ).writeText( trim( stdErrForSuite ), XmlFormatting::Newline );
+    }
 
-    struct ConsoleReporter : StreamingReporterBase {
-        ConsoleReporter( ReporterConfig const& _config )
-        :   StreamingReporterBase( _config ),
-            m_headerPrinted( false )
-        {}
+    void JunitReporter::writeTestCase( TestCaseNode const& testCaseNode ) {
+        TestCaseStats const& stats = testCaseNode.value;
 
-        virtual ~ConsoleReporter() CATCH_OVERRIDE;
-        static std::string getDescription() {
-            return "Reports test results as plain lines of text";
-        }
+        // All test cases have exactly one section - which represents the
+        // test case itself. That section may have 0-n nested sections
+        assert( testCaseNode.children.size() == 1 );
+        SectionNode const& rootSection = *testCaseNode.children.front();
 
-        virtual void noMatchingTestCases( std::string const& spec ) CATCH_OVERRIDE {
-            stream << "No test cases matched '" << spec << '\'' << std::endl;
-        }
+        std::string className = stats.testInfo.className;
 
-        virtual void assertionStarting( AssertionInfo const& ) CATCH_OVERRIDE {
+        if( className.empty() ) {
+            className = fileNameTag(stats.testInfo.tags);
+            if ( className.empty() )
+                className = "global";
         }
 
-        virtual bool assertionEnded( AssertionStats const& _assertionStats ) CATCH_OVERRIDE {
-            AssertionResult const& result = _assertionStats.assertionResult;
-
-            bool includeResults = m_config->includeSuccessfulResults() || !result.isOk();
-
-            // Drop out if result was successful but we're not printing them.
-            if( !includeResults && result.getResultType() != ResultWas::Warning )
-                return false;
+        if ( !m_config->name().empty() )
+            className = m_config->name() + "." + className;
 
-            lazyPrint();
+        writeSection( className, "", rootSection, stats.testInfo.okToFail() );
+    }
 
-            AssertionPrinter printer( stream, _assertionStats, includeResults );
-            printer.print();
-            stream << std::endl;
-            return true;
-        }
+    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;
 
-        virtual void sectionStarting( SectionInfo const& _sectionInfo ) CATCH_OVERRIDE {
-            m_headerPrinted = false;
-            StreamingReporterBase::sectionStarting( _sectionInfo );
-        }
-        virtual void sectionEnded( SectionStats const& _sectionStats ) CATCH_OVERRIDE {
-            if( _sectionStats.missingAssertions ) {
-                lazyPrint();
-                Colour colour( Colour::ResultError );
-                if( m_sectionStack.size() > 1 )
-                    stream << "\nNo assertions in section";
-                else
-                    stream << "\nNo assertions in test case";
-                stream << " '" << _sectionStats.sectionInfo.name << "'\n" << std::endl;
+        if( !sectionNode.assertions.empty() ||
+            !sectionNode.stdOut.empty() ||
+            !sectionNode.stdErr.empty() ) {
+            XmlWriter::ScopedElement e = xml.scopedElement( "testcase" );
+            if( className.empty() ) {
+                xml.writeAttribute( "classname", name );
+                xml.writeAttribute( "name", "root" );
             }
-            if( m_config->showDurations() == ShowDurations::Always ) {
-                stream << getFormattedDuration(_sectionStats.durationInSeconds) << " s: " << _sectionStats.sectionInfo.name << std::endl;
+            else {
+                xml.writeAttribute( "classname", className );
+                xml.writeAttribute( "name", name );
             }
-            if( m_headerPrinted ) {
-                m_headerPrinted = false;
+            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");
             }
-            StreamingReporterBase::sectionEnded( _sectionStats );
-        }
 
-        virtual void testCaseEnded( TestCaseStats const& _testCaseStats ) CATCH_OVERRIDE {
-            StreamingReporterBase::testCaseEnded( _testCaseStats );
-            m_headerPrinted = false;
+            writeAssertions( sectionNode );
+
+            if( !sectionNode.stdOut.empty() )
+                xml.scopedElement( "system-out" ).writeText( trim( sectionNode.stdOut ), XmlFormatting::Newline );
+            if( !sectionNode.stdErr.empty() )
+                xml.scopedElement( "system-err" ).writeText( trim( sectionNode.stdErr ), XmlFormatting::Newline );
         }
-        virtual void testGroupEnded( TestGroupStats const& _testGroupStats ) CATCH_OVERRIDE {
-            if( currentGroupInfo.used ) {
-                printSummaryDivider();
-                stream << "Summary for group '" << _testGroupStats.groupInfo.name << "':\n";
-                printTotals( _testGroupStats.totals );
-                stream << '\n' << std::endl;
+        for( auto const& childNode : sectionNode.childSections )
+            if( className.empty() )
+                writeSection( name, "", *childNode, testOkToFail );
+            else
+                writeSection( className, name, *childNode, testOkToFail );
+    }
+
+    void JunitReporter::writeAssertions( SectionNode const& sectionNode ) {
+        for( auto const& assertion : sectionNode.assertions )
+            writeAssertion( assertion );
+    }
+
+    void JunitReporter::writeAssertion( AssertionStats const& stats ) {
+        AssertionResult const& result = stats.assertionResult;
+        if( !result.isOk() ) {
+            std::string elementName;
+            switch( result.getResultType() ) {
+                case ResultWas::ThrewException:
+                case ResultWas::FatalErrorCondition:
+                    elementName = "error";
+                    break;
+                case ResultWas::ExplicitFailure:
+                case ResultWas::ExpressionFailed:
+                case ResultWas::DidntThrowException:
+                    elementName = "failure";
+                    break;
+
+                // We should never see these here:
+                case ResultWas::Info:
+                case ResultWas::Warning:
+                case ResultWas::Ok:
+                case ResultWas::Unknown:
+                case ResultWas::FailureBit:
+                case ResultWas::Exception:
+                    elementName = "internalError";
+                    break;
             }
-            StreamingReporterBase::testGroupEnded( _testGroupStats );
-        }
-        virtual void testRunEnded( TestRunStats const& _testRunStats ) CATCH_OVERRIDE {
-            printTotalsDivider( _testRunStats.totals );
-            printTotals( _testRunStats.totals );
-            stream << std::endl;
-            StreamingReporterBase::testRunEnded( _testRunStats );
-        }
 
-    private:
+            XmlWriter::ScopedElement e = xml.scopedElement( elementName );
 
-        class AssertionPrinter {
-            void operator= ( AssertionPrinter const& );
-        public:
-            AssertionPrinter( std::ostream& _stream, AssertionStats const& _stats, bool _printInfoMessages )
-            :   stream( _stream ),
-                stats( _stats ),
-                result( _stats.assertionResult ),
-                colour( Colour::None ),
-                message( result.getMessage() ),
-                messages( _stats.infoMessages ),
-                printInfoMessages( _printInfoMessages )
-            {
-                switch( result.getResultType() ) {
-                    case ResultWas::Ok:
-                        colour = Colour::Success;
-                        passOrFail = "PASSED";
-                        //if( result.hasMessage() )
-                        if( _stats.infoMessages.size() == 1 )
-                            messageLabel = "with message";
-                        if( _stats.infoMessages.size() > 1 )
-                            messageLabel = "with messages";
-                        break;
-                    case ResultWas::ExpressionFailed:
-                        if( result.isOk() ) {
-                            colour = Colour::Success;
-                            passOrFail = "FAILED - but was ok";
-                        }
-                        else {
-                            colour = Colour::Error;
-                            passOrFail = "FAILED";
-                        }
-                        if( _stats.infoMessages.size() == 1 )
-                            messageLabel = "with message";
-                        if( _stats.infoMessages.size() > 1 )
-                            messageLabel = "with messages";
-                        break;
-                    case ResultWas::ThrewException:
-                        colour = Colour::Error;
-                        passOrFail = "FAILED";
-                        messageLabel = "due to unexpected exception with ";
-                        if (_stats.infoMessages.size() == 1)
-                            messageLabel += "message";
-                        if (_stats.infoMessages.size() > 1)
-                            messageLabel += "messages";
-                        break;
-                    case ResultWas::FatalErrorCondition:
-                        colour = Colour::Error;
-                        passOrFail = "FAILED";
-                        messageLabel = "due to a fatal error condition";
-                        break;
-                    case ResultWas::DidntThrowException:
-                        colour = Colour::Error;
-                        passOrFail = "FAILED";
-                        messageLabel = "because no exception was thrown where one was expected";
-                        break;
-                    case ResultWas::Info:
-                        messageLabel = "info";
-                        break;
-                    case ResultWas::Warning:
-                        messageLabel = "warning";
-                        break;
-                    case ResultWas::ExplicitFailure:
-                        passOrFail = "FAILED";
-                        colour = Colour::Error;
-                        if( _stats.infoMessages.size() == 1 )
-                            messageLabel = "explicitly with message";
-                        if( _stats.infoMessages.size() > 1 )
-                            messageLabel = "explicitly with messages";
-                        break;
-                    // These cases are here to prevent compiler warnings
-                    case ResultWas::Unknown:
-                    case ResultWas::FailureBit:
-                    case ResultWas::Exception:
-                        passOrFail = "** internal error **";
-                        colour = Colour::Error;
-                        break;
-                }
-            }
+            xml.writeAttribute( "message", result.getExpression() );
+            xml.writeAttribute( "type", result.getTestMacroName() );
 
-            void print() const {
-                printSourceInfo();
-                if( stats.totals.assertions.total() > 0 ) {
-                    if( result.isOk() )
-                        stream << '\n';
-                    printResultType();
-                    printOriginalExpression();
-                    printReconstructedExpression();
+            ReusableStringStream rss;
+            if (stats.totals.assertions.total() > 0) {
+                rss << "FAILED" << ":\n";
+                if (result.hasExpression()) {
+                    rss << "  ";
+                    rss << result.getExpressionInMacro();
+                    rss << '\n';
                 }
-                else {
-                    stream << '\n';
+                if (result.hasExpandedExpression()) {
+                    rss << "with expansion:\n";
+                    rss << Column(result.getExpandedExpression()).indent(2) << '\n';
                 }
-                printMessage();
+            } else {
+                rss << '\n';
             }
 
-        private:
-            void printResultType() const {
-                if( !passOrFail.empty() ) {
-                    Colour colourGuard( colour );
-                    stream << passOrFail << ":\n";
-                }
-            }
-            void printOriginalExpression() const {
-                if( result.hasExpression() ) {
-                    Colour colourGuard( Colour::OriginalExpression );
-                    stream  << "  ";
-                    stream << result.getExpressionInMacro();
-                    stream << '\n';
-                }
-            }
-            void printReconstructedExpression() const {
-                if( result.hasExpandedExpression() ) {
-                    stream << "with expansion:\n";
-                    Colour colourGuard( Colour::ReconstructedExpression );
-                    stream << Text( result.getExpandedExpression(), TextAttributes().setIndent(2) ) << '\n';
-                }
-            }
-            void printMessage() const {
-                if( !messageLabel.empty() )
-                    stream << messageLabel << ':' << '\n';
-                for( std::vector<MessageInfo>::const_iterator it = messages.begin(), itEnd = messages.end();
-                        it != itEnd;
-                        ++it ) {
-                    // If this assertion is a warning ignore any INFO messages
-                    if( printInfoMessages || it->type != ResultWas::Info )
-                        stream << Text( it->message, TextAttributes().setIndent(2) ) << '\n';
-                }
-            }
-            void printSourceInfo() const {
-                Colour colourGuard( Colour::FileName );
-                stream << result.getSourceInfo() << ": ";
-            }
+            if( !result.getMessage().empty() )
+                rss << result.getMessage() << '\n';
+            for( auto const& msg : stats.infoMessages )
+                if( msg.type == ResultWas::Info )
+                    rss << msg.message << '\n';
 
-            std::ostream& stream;
-            AssertionStats const& stats;
-            AssertionResult const& result;
-            Colour::Code colour;
-            std::string passOrFail;
-            std::string messageLabel;
-            std::string message;
-            std::vector<MessageInfo> messages;
-            bool printInfoMessages;
-        };
+            rss << "at " << result.getSourceInfo();
+            xml.writeText( rss.str(), XmlFormatting::Newline );
+        }
+    }
 
-        void lazyPrint() {
+    CATCH_REGISTER_REPORTER( "junit", JunitReporter )
 
-            if( !currentTestRunInfo.used )
-                lazyPrintRunInfo();
-            if( !currentGroupInfo.used )
-                lazyPrintGroupInfo();
+} // end namespace Catch
+// end catch_reporter_junit.cpp
+// start catch_reporter_listening.cpp
 
-            if( !m_headerPrinted ) {
-                printTestCaseAndSectionHeader();
-                m_headerPrinted = true;
-            }
-        }
-        void lazyPrintRunInfo() {
-            stream  << '\n' << getLineOfChars<'~'>() << '\n';
-            Colour colour( Colour::SecondaryText );
-            stream  << currentTestRunInfo->name
-                    << " is a Catch v"  << libraryVersion() << " host application.\n"
-                    << "Run with -? for options\n\n";
+#include <cassert>
 
-            if( m_config->rngSeed() != 0 )
-                stream << "Randomness seeded to: " << m_config->rngSeed() << "\n\n";
+namespace Catch {
 
-            currentTestRunInfo.used = true;
-        }
-        void lazyPrintGroupInfo() {
-            if( !currentGroupInfo->name.empty() && currentGroupInfo->groupsCounts > 1 ) {
-                printClosedHeader( "Group: " + currentGroupInfo->name );
-                currentGroupInfo.used = true;
-            }
-        }
-        void printTestCaseAndSectionHeader() {
-            assert( !m_sectionStack.empty() );
-            printOpenHeader( currentTestCaseInfo->name );
+    ListeningReporter::ListeningReporter() {
+        // We will assume that listeners will always want all assertions
+        m_preferences.shouldReportAllAssertions = true;
+    }
 
-            if( m_sectionStack.size() > 1 ) {
-                Colour colourGuard( Colour::Headers );
+    void ListeningReporter::addListener( IStreamingReporterPtr&& listener ) {
+        m_listeners.push_back( std::move( listener ) );
+    }
 
-                std::vector<SectionInfo>::const_iterator
-                    it = m_sectionStack.begin()+1, // Skip first section (test case)
-                    itEnd = m_sectionStack.end();
-                for( ; it != itEnd; ++it )
-                    printHeaderString( it->name, 2 );
-            }
+    void ListeningReporter::addReporter(IStreamingReporterPtr&& reporter) {
+        assert(!m_reporter && "Listening reporter can wrap only 1 real reporter");
+        m_reporter = std::move( reporter );
+        m_preferences.shouldRedirectStdOut = m_reporter->getPreferences().shouldRedirectStdOut;
+    }
 
-            SourceLineInfo lineInfo = m_sectionStack.back().lineInfo;
+    ReporterPreferences ListeningReporter::getPreferences() const {
+        return m_preferences;
+    }
 
-            if( !lineInfo.empty() ){
-                stream << getLineOfChars<'-'>() << '\n';
-                Colour colourGuard( Colour::FileName );
-                stream << lineInfo << '\n';
-            }
-            stream << getLineOfChars<'.'>() << '\n' << std::endl;
+    std::set<Verbosity> ListeningReporter::getSupportedVerbosities() {
+        return std::set<Verbosity>{ };
+    }
+
+    void ListeningReporter::noMatchingTestCases( std::string const& spec ) {
+        for ( auto const& listener : m_listeners ) {
+            listener->noMatchingTestCases( spec );
         }
+        m_reporter->noMatchingTestCases( spec );
+    }
 
-        void printClosedHeader( std::string const& _name ) {
-            printOpenHeader( _name );
-            stream << getLineOfChars<'.'>() << '\n';
+    void ListeningReporter::reportInvalidArguments(std::string const&arg){
+        for ( auto const& listener : m_listeners ) {
+            listener->reportInvalidArguments( arg );
         }
-        void printOpenHeader( std::string const& _name ) {
-            stream  << getLineOfChars<'-'>() << '\n';
-            {
-                Colour colourGuard( Colour::Headers );
-                printHeaderString( _name );
-            }
+        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 ) {
+        for ( auto const& listener : m_listeners ) {
+            listener->benchmarkEnded( benchmarkStats );
         }
+        m_reporter->benchmarkEnded( benchmarkStats );
+    }
 
-        // if string has a : in first line will set indent to follow it on
-        // subsequent lines
-        void printHeaderString( std::string const& _string, std::size_t indent = 0 ) {
-            std::size_t i = _string.find( ": " );
-            if( i != std::string::npos )
-                i+=2;
-            else
-                i = 0;
-            stream << Text( _string, TextAttributes()
-                                        .setIndent( indent+i)
-                                        .setInitialIndent( indent ) ) << '\n';
+	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 );
         }
+        m_reporter->testRunStarting( testRunInfo );
+    }
 
-        struct SummaryColumn {
+    void ListeningReporter::testGroupStarting( GroupInfo const& groupInfo ) {
+        for ( auto const& listener : m_listeners ) {
+            listener->testGroupStarting( groupInfo );
+        }
+        m_reporter->testGroupStarting( groupInfo );
+    }
 
-            SummaryColumn( std::string const& _label, Colour::Code _colour )
-            :   label( _label ),
-                colour( _colour )
-            {}
-            SummaryColumn addRow( std::size_t count ) {
-                std::ostringstream oss;
-                oss << count;
-                std::string row = oss.str();
-                for( std::vector<std::string>::iterator it = rows.begin(); it != rows.end(); ++it ) {
-                    while( it->size() < row.size() )
-                        *it = ' ' + *it;
-                    while( it->size() > row.size() )
-                        row = ' ' + row;
-                }
-                rows.push_back( row );
-                return *this;
-            }
+    void ListeningReporter::testCaseStarting( TestCaseInfo const& testInfo ) {
+        for ( auto const& listener : m_listeners ) {
+            listener->testCaseStarting( testInfo );
+        }
+        m_reporter->testCaseStarting( testInfo );
+    }
 
-            std::string label;
-            Colour::Code colour;
-            std::vector<std::string> rows;
+    void ListeningReporter::sectionStarting( SectionInfo const& sectionInfo ) {
+        for ( auto const& listener : m_listeners ) {
+            listener->sectionStarting( sectionInfo );
+        }
+        m_reporter->sectionStarting( sectionInfo );
+    }
 
-        };
+    void ListeningReporter::assertionStarting( AssertionInfo const& assertionInfo ) {
+        for ( auto const& listener : m_listeners ) {
+            listener->assertionStarting( assertionInfo );
+        }
+        m_reporter->assertionStarting( assertionInfo );
+    }
 
-        void printTotals( Totals const& totals ) {
-            if( totals.testCases.total() == 0 ) {
-                stream << Colour( Colour::Warning ) << "No tests ran\n";
-            }
-            else if( totals.assertions.total() > 0 && totals.testCases.allPassed() ) {
-                stream << Colour( Colour::ResultSuccess ) << "All tests passed";
-                stream << " ("
-                        << pluralise( totals.assertions.passed, "assertion" ) << " in "
-                        << pluralise( totals.testCases.passed, "test case" ) << ')'
-                        << '\n';
-            }
-            else {
+    // The return value indicates if the messages buffer should be cleared:
+    bool ListeningReporter::assertionEnded( AssertionStats const& assertionStats ) {
+        for( auto const& listener : m_listeners ) {
+            static_cast<void>( listener->assertionEnded( assertionStats ) );
+        }
+        return m_reporter->assertionEnded( assertionStats );
+    }
 
-                std::vector<SummaryColumn> columns;
-                columns.push_back( SummaryColumn( "", Colour::None )
-                                        .addRow( totals.testCases.total() )
-                                        .addRow( totals.assertions.total() ) );
-                columns.push_back( SummaryColumn( "passed", Colour::Success )
-                                        .addRow( totals.testCases.passed )
-                                        .addRow( totals.assertions.passed ) );
-                columns.push_back( SummaryColumn( "failed", Colour::ResultError )
-                                        .addRow( totals.testCases.failed )
-                                        .addRow( totals.assertions.failed ) );
-                columns.push_back( SummaryColumn( "failed as expected", Colour::ResultExpectedFailure )
-                                        .addRow( totals.testCases.failedButOk )
-                                        .addRow( totals.assertions.failedButOk ) );
-
-                printSummaryRow( "test cases", columns, 0 );
-                printSummaryRow( "assertions", columns, 1 );
-            }
-        }
-        void printSummaryRow( std::string const& label, std::vector<SummaryColumn> const& cols, std::size_t row ) {
-            for( std::vector<SummaryColumn>::const_iterator it = cols.begin(); it != cols.end(); ++it ) {
-                std::string value = it->rows[row];
-                if( it->label.empty() ) {
-                    stream << label << ": ";
-                    if( value != "0" )
-                        stream << value;
-                    else
-                        stream << Colour( Colour::Warning ) << "- none -";
-                }
-                else if( value != "0" ) {
-                    stream  << Colour( Colour::LightGrey ) << " | ";
-                    stream  << Colour( it->colour )
-                            << value << ' ' << it->label;
-                }
-            }
-            stream << '\n';
+    void ListeningReporter::sectionEnded( SectionStats const& sectionStats ) {
+        for ( auto const& listener : m_listeners ) {
+            listener->sectionEnded( sectionStats );
         }
+        m_reporter->sectionEnded( sectionStats );
+    }
 
-        static std::size_t makeRatio( std::size_t number, std::size_t total ) {
-            std::size_t ratio = total > 0 ? CATCH_CONFIG_CONSOLE_WIDTH * number/ total : 0;
-            return ( ratio == 0 && number > 0 ) ? 1 : ratio;
+    void ListeningReporter::testCaseEnded( TestCaseStats const& testCaseStats ) {
+        for ( auto const& listener : m_listeners ) {
+            listener->testCaseEnded( testCaseStats );
         }
-        static std::size_t& findMax( std::size_t& i, std::size_t& j, std::size_t& k ) {
-            if( i > j && i > k )
-                return i;
-            else if( j > k )
-                return j;
-            else
-                return k;
-        }
-
-        void printTotalsDivider( Totals const& totals ) {
-            if( totals.testCases.total() > 0 ) {
-                std::size_t failedRatio = makeRatio( totals.testCases.failed, totals.testCases.total() );
-                std::size_t failedButOkRatio = makeRatio( totals.testCases.failedButOk, totals.testCases.total() );
-                std::size_t passedRatio = makeRatio( totals.testCases.passed, totals.testCases.total() );
-                while( failedRatio + failedButOkRatio + passedRatio < CATCH_CONFIG_CONSOLE_WIDTH-1 )
-                    findMax( failedRatio, failedButOkRatio, passedRatio )++;
-                while( failedRatio + failedButOkRatio + passedRatio > CATCH_CONFIG_CONSOLE_WIDTH-1 )
-                    findMax( failedRatio, failedButOkRatio, passedRatio )--;
-
-                stream << Colour( Colour::Error ) << std::string( failedRatio, '=' );
-                stream << Colour( Colour::ResultExpectedFailure ) << std::string( failedButOkRatio, '=' );
-                if( totals.testCases.allPassed() )
-                    stream << Colour( Colour::ResultSuccess ) << std::string( passedRatio, '=' );
-                else
-                    stream << Colour( Colour::Success ) << std::string( passedRatio, '=' );
-            }
-            else {
-                stream << Colour( Colour::Warning ) << std::string( CATCH_CONFIG_CONSOLE_WIDTH-1, '=' );
-            }
-            stream << '\n';
+        m_reporter->testCaseEnded( testCaseStats );
+    }
+
+    void ListeningReporter::testGroupEnded( TestGroupStats const& testGroupStats ) {
+        for ( auto const& listener : m_listeners ) {
+            listener->testGroupEnded( testGroupStats );
         }
-        void printSummaryDivider() {
-            stream << getLineOfChars<'-'>() << '\n';
+        m_reporter->testGroupEnded( testGroupStats );
+    }
+
+    void ListeningReporter::testRunEnded( TestRunStats const& testRunStats ) {
+        for ( auto const& listener : m_listeners ) {
+            listener->testRunEnded( testRunStats );
         }
+        m_reporter->testRunEnded( testRunStats );
+    }
 
-    private:
-        bool m_headerPrinted;
-    };
+    void ListeningReporter::skipTest( TestCaseInfo const& testInfo ) {
+        for ( auto const& listener : m_listeners ) {
+            listener->skipTest( testInfo );
+        }
+        m_reporter->skipTest( testInfo );
+    }
 
-    INTERNAL_CATCH_REGISTER_REPORTER( "console", ConsoleReporter )
+    bool ListeningReporter::isMulti() const {
+        return true;
+    }
 
 } // end namespace Catch
+// end catch_reporter_listening.cpp
+// start catch_reporter_xml.cpp
 
-// #included from: ../reporters/catch_reporter_compact.hpp
-#define TWOBLUECUBES_CATCH_REPORTER_COMPACT_HPP_INCLUDED
+#if defined(_MSC_VER)
+#pragma warning(push)
+#pragma warning(disable:4061) // Not all labels are EXPLICITLY handled in switch
+                              // Note that 4062 (not all labels are handled
+                              // and default is missing) is enabled
+#endif
 
 namespace Catch {
+    XmlReporter::XmlReporter( ReporterConfig const& _config )
+    :   StreamingReporterBase( _config ),
+        m_xml(_config.stream())
+    {
+        m_reporterPrefs.shouldRedirectStdOut = true;
+        m_reporterPrefs.shouldReportAllAssertions = true;
+    }
 
-    struct CompactReporter : StreamingReporterBase {
-
-        CompactReporter( ReporterConfig const& _config )
-        : StreamingReporterBase( _config )
-        {}
-
-        virtual ~CompactReporter();
-
-        static std::string getDescription() {
-            return "Reports test results on a single line, suitable for IDEs";
-        }
+    XmlReporter::~XmlReporter() = default;
 
-        virtual ReporterPreferences getPreferences() const {
-            ReporterPreferences prefs;
-            prefs.shouldRedirectStdOut = false;
-            return prefs;
-        }
+    std::string XmlReporter::getDescription() {
+        return "Reports test results as an XML document";
+    }
 
-        virtual void noMatchingTestCases( std::string const& spec ) {
-            stream << "No test cases matched '" << spec << '\'' << std::endl;
-        }
+    std::string XmlReporter::getStylesheetRef() const {
+        return std::string();
+    }
 
-        virtual void assertionStarting( AssertionInfo const& ) {}
+    void XmlReporter::writeSourceInfo( SourceLineInfo const& sourceInfo ) {
+        m_xml
+            .writeAttribute( "filename", sourceInfo.file )
+            .writeAttribute( "line", sourceInfo.line );
+    }
 
-        virtual bool assertionEnded( AssertionStats const& _assertionStats ) {
-            AssertionResult const& result = _assertionStats.assertionResult;
+    void XmlReporter::noMatchingTestCases( std::string const& s ) {
+        StreamingReporterBase::noMatchingTestCases( s );
+    }
 
-            bool printInfoMessages = true;
+    void XmlReporter::testRunStarting( TestRunInfo const& testInfo ) {
+        StreamingReporterBase::testRunStarting( testInfo );
+        std::string stylesheetRef = getStylesheetRef();
+        if( !stylesheetRef.empty() )
+            m_xml.writeStylesheetRef( stylesheetRef );
+        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() );
+    }
 
-            // Drop out if result was successful and we're not printing those
-            if( !m_config->includeSuccessfulResults() && result.isOk() ) {
-                if( result.getResultType() != ResultWas::Warning )
-                    return false;
-                printInfoMessages = false;
-            }
+    void XmlReporter::testGroupStarting( GroupInfo const& groupInfo ) {
+        StreamingReporterBase::testGroupStarting( groupInfo );
+        m_xml.startElement( "Group" )
+            .writeAttribute( "name", groupInfo.name );
+    }
 
-            AssertionPrinter printer( stream, _assertionStats, printInfoMessages );
-            printer.print();
+    void XmlReporter::testCaseStarting( TestCaseInfo const& testInfo ) {
+        StreamingReporterBase::testCaseStarting(testInfo);
+        m_xml.startElement( "TestCase" )
+            .writeAttribute( "name", trim( testInfo.name ) )
+            .writeAttribute( "description", testInfo.description )
+            .writeAttribute( "tags", testInfo.tagsAsString() );
 
-            stream << std::endl;
-            return true;
-        }
+        writeSourceInfo( testInfo.lineInfo );
 
-        virtual void sectionEnded(SectionStats const& _sectionStats) CATCH_OVERRIDE {
-            if (m_config->showDurations() == ShowDurations::Always) {
-                stream << getFormattedDuration(_sectionStats.durationInSeconds) << " s: " << _sectionStats.sectionInfo.name << std::endl;
-            }
-        }
+        if ( m_config->showDurations() == ShowDurations::Always )
+            m_testCaseTimer.start();
+        m_xml.ensureTagClosed();
+    }
 
-        virtual void testRunEnded( TestRunStats const& _testRunStats ) {
-            printTotals( _testRunStats.totals );
-            stream << '\n' << std::endl;
-            StreamingReporterBase::testRunEnded( _testRunStats );
+    void XmlReporter::sectionStarting( SectionInfo const& sectionInfo ) {
+        StreamingReporterBase::sectionStarting( sectionInfo );
+        if( m_sectionDepth++ > 0 ) {
+            m_xml.startElement( "Section" )
+                .writeAttribute( "name", trim( sectionInfo.name ) );
+            writeSourceInfo( sectionInfo.lineInfo );
+            m_xml.ensureTagClosed();
         }
+    }
 
-    private:
-        class AssertionPrinter {
-            void operator= ( AssertionPrinter const& );
-        public:
-            AssertionPrinter( std::ostream& _stream, AssertionStats const& _stats, bool _printInfoMessages )
-            : stream( _stream )
-            , stats( _stats )
-            , result( _stats.assertionResult )
-            , messages( _stats.infoMessages )
-            , itMessage( _stats.infoMessages.begin() )
-            , printInfoMessages( _printInfoMessages )
-            {}
-
-            void print() {
-                printSourceInfo();
-
-                itMessage = messages.begin();
-
-                switch( result.getResultType() ) {
-                    case ResultWas::Ok:
-                        printResultType( Colour::ResultSuccess, passedString() );
-                        printOriginalExpression();
-                        printReconstructedExpression();
-                        if ( ! result.hasExpression() )
-                            printRemainingMessages( Colour::None );
-                        else
-                            printRemainingMessages();
-                        break;
-                    case ResultWas::ExpressionFailed:
-                        if( result.isOk() )
-                            printResultType( Colour::ResultSuccess, failedString() + std::string( " - but was ok" ) );
-                        else
-                            printResultType( Colour::Error, failedString() );
-                        printOriginalExpression();
-                        printReconstructedExpression();
-                        printRemainingMessages();
-                        break;
-                    case ResultWas::ThrewException:
-                        printResultType( Colour::Error, failedString() );
-                        printIssue( "unexpected exception with message:" );
-                        printMessage();
-                        printExpressionWas();
-                        printRemainingMessages();
-                        break;
-                    case ResultWas::FatalErrorCondition:
-                        printResultType( Colour::Error, failedString() );
-                        printIssue( "fatal error condition with message:" );
-                        printMessage();
-                        printExpressionWas();
-                        printRemainingMessages();
-                        break;
-                    case ResultWas::DidntThrowException:
-                        printResultType( Colour::Error, failedString() );
-                        printIssue( "expected exception, got none" );
-                        printExpressionWas();
-                        printRemainingMessages();
-                        break;
-                    case ResultWas::Info:
-                        printResultType( Colour::None, "info" );
-                        printMessage();
-                        printRemainingMessages();
-                        break;
-                    case ResultWas::Warning:
-                        printResultType( Colour::None, "warning" );
-                        printMessage();
-                        printRemainingMessages();
-                        break;
-                    case ResultWas::ExplicitFailure:
-                        printResultType( Colour::Error, failedString() );
-                        printIssue( "explicitly" );
-                        printRemainingMessages( Colour::None );
-                        break;
-                    // These cases are here to prevent compiler warnings
-                    case ResultWas::Unknown:
-                    case ResultWas::FailureBit:
-                    case ResultWas::Exception:
-                        printResultType( Colour::Error, "** internal error **" );
-                        break;
-                }
-            }
-
-        private:
-            // Colour::LightGrey
+    void XmlReporter::assertionStarting( AssertionInfo const& ) { }
 
-            static Colour::Code dimColour() { return Colour::FileName; }
+    bool XmlReporter::assertionEnded( AssertionStats const& assertionStats ) {
 
-#ifdef CATCH_PLATFORM_MAC
-            static const char* failedString() { return "FAILED"; }
-            static const char* passedString() { return "PASSED"; }
-#else
-            static const char* failedString() { return "failed"; }
-            static const char* passedString() { return "passed"; }
-#endif
+        AssertionResult const& result = assertionStats.assertionResult;
 
-            void printSourceInfo() const {
-                Colour colourGuard( Colour::FileName );
-                stream << result.getSourceInfo() << ':';
-            }
+        bool includeResults = m_config->includeSuccessfulResults() || !result.isOk();
 
-            void printResultType( Colour::Code colour, std::string const& passOrFail ) const {
-                if( !passOrFail.empty() ) {
-                    {
-                        Colour colourGuard( colour );
-                        stream << ' ' << passOrFail;
-                    }
-                    stream << ':';
+        if( includeResults || result.getResultType() == ResultWas::Warning ) {
+            // Print any info messages in <Info> tags.
+            for( auto const& msg : assertionStats.infoMessages ) {
+                if( msg.type == ResultWas::Info && includeResults ) {
+                    m_xml.scopedElement( "Info" )
+                            .writeText( msg.message );
+                } else if ( msg.type == ResultWas::Warning ) {
+                    m_xml.scopedElement( "Warning" )
+                            .writeText( msg.message );
                 }
             }
+        }
 
-            void printIssue( std::string const& issue ) const {
-                stream << ' ' << issue;
-            }
-
-            void printExpressionWas() {
-                if( result.hasExpression() ) {
-                    stream << ';';
-                    {
-                        Colour colour( dimColour() );
-                        stream << " expression was:";
-                    }
-                    printOriginalExpression();
-                }
-            }
+        // Drop out if result was successful but we're not printing them.
+        if( !includeResults && result.getResultType() != ResultWas::Warning )
+            return true;
 
-            void printOriginalExpression() const {
-                if( result.hasExpression() ) {
-                    stream << ' ' << result.getExpression();
-                }
-            }
+        // Print the expression if there is one.
+        if( result.hasExpression() ) {
+            m_xml.startElement( "Expression" )
+                .writeAttribute( "success", result.succeeded() )
+                .writeAttribute( "type", result.getTestMacroName() );
 
-            void printReconstructedExpression() const {
-                if( result.hasExpandedExpression() ) {
-                    {
-                        Colour colour( dimColour() );
-                        stream << " for: ";
-                    }
-                    stream << result.getExpandedExpression();
-                }
-            }
+            writeSourceInfo( result.getSourceInfo() );
 
-            void printMessage() {
-                if ( itMessage != messages.end() ) {
-                    stream << " '" << itMessage->message << '\'';
-                    ++itMessage;
-                }
-            }
+            m_xml.scopedElement( "Original" )
+                .writeText( result.getExpression() );
+            m_xml.scopedElement( "Expanded" )
+                .writeText( result.getExpandedExpression() );
+        }
 
-            void printRemainingMessages( Colour::Code colour = dimColour() ) {
-                if ( itMessage == messages.end() )
-                    return;
+        // And... Print a result applicable to each result type.
+        switch( result.getResultType() ) {
+            case ResultWas::ThrewException:
+                m_xml.startElement( "Exception" );
+                writeSourceInfo( result.getSourceInfo() );
+                m_xml.writeText( result.getMessage() );
+                m_xml.endElement();
+                break;
+            case ResultWas::FatalErrorCondition:
+                m_xml.startElement( "FatalErrorCondition" );
+                writeSourceInfo( result.getSourceInfo() );
+                m_xml.writeText( result.getMessage() );
+                m_xml.endElement();
+                break;
+            case ResultWas::Info:
+                m_xml.scopedElement( "Info" )
+                    .writeText( result.getMessage() );
+                break;
+            case ResultWas::Warning:
+                // Warning will already have been written
+                break;
+            case ResultWas::ExplicitFailure:
+                m_xml.startElement( "Failure" );
+                writeSourceInfo( result.getSourceInfo() );
+                m_xml.writeText( result.getMessage() );
+                m_xml.endElement();
+                break;
+            default:
+                break;
+        }
 
-                // using messages.end() directly yields compilation error:
-                std::vector<MessageInfo>::const_iterator itEnd = messages.end();
-                const std::size_t N = static_cast<std::size_t>( std::distance( itMessage, itEnd ) );
+        if( result.hasExpression() )
+            m_xml.endElement();
 
-                {
-                    Colour colourGuard( colour );
-                    stream << " with " << pluralise( N, "message" ) << ':';
-                }
+        return true;
+    }
 
-                for(; itMessage != itEnd; ) {
-                    // If this assertion is a warning ignore any INFO messages
-                    if( printInfoMessages || itMessage->type != ResultWas::Info ) {
-                        stream << " '" << itMessage->message << '\'';
-                        if ( ++itMessage != itEnd ) {
-                            Colour colourGuard( dimColour() );
-                            stream << " and";
-                        }
-                    }
-                }
-            }
+    void XmlReporter::sectionEnded( SectionStats const& sectionStats ) {
+        StreamingReporterBase::sectionEnded( sectionStats );
+        if( --m_sectionDepth > 0 ) {
+            XmlWriter::ScopedElement e = m_xml.scopedElement( "OverallResults" );
+            e.writeAttribute( "successes", sectionStats.assertions.passed );
+            e.writeAttribute( "failures", sectionStats.assertions.failed );
+            e.writeAttribute( "expectedFailures", sectionStats.assertions.failedButOk );
 
-        private:
-            std::ostream& stream;
-            AssertionStats const& stats;
-            AssertionResult const& result;
-            std::vector<MessageInfo> messages;
-            std::vector<MessageInfo>::const_iterator itMessage;
-            bool printInfoMessages;
-        };
+            if ( m_config->showDurations() == ShowDurations::Always )
+                e.writeAttribute( "durationInSeconds", sectionStats.durationInSeconds );
 
-        // Colour, message variants:
-        // - white: No tests ran.
-        // -   red: Failed [both/all] N test cases, failed [both/all] M assertions.
-        // - white: Passed [both/all] N test cases (no assertions).
-        // -   red: Failed N tests cases, failed M assertions.
-        // - green: Passed [both/all] N tests cases with M assertions.
-
-        std::string bothOrAll( std::size_t count ) const {
-            return count == 1 ? std::string() : count == 2 ? "both " : "all " ;
-        }
-
-        void printTotals( const Totals& totals ) const {
-            if( totals.testCases.total() == 0 ) {
-                stream << "No tests ran.";
-            }
-            else if( totals.testCases.failed == totals.testCases.total() ) {
-                Colour colour( Colour::ResultError );
-                const std::string qualify_assertions_failed =
-                    totals.assertions.failed == totals.assertions.total() ?
-                        bothOrAll( totals.assertions.failed ) : std::string();
-                stream <<
-                    "Failed " << bothOrAll( totals.testCases.failed )
-                              << pluralise( totals.testCases.failed, "test case"  ) << ", "
-                    "failed " << qualify_assertions_failed <<
-                                 pluralise( totals.assertions.failed, "assertion" ) << '.';
-            }
-            else if( totals.assertions.total() == 0 ) {
-                stream <<
-                    "Passed " << bothOrAll( totals.testCases.total() )
-                              << pluralise( totals.testCases.total(), "test case" )
-                              << " (no assertions).";
-            }
-            else if( totals.assertions.failed ) {
-                Colour colour( Colour::ResultError );
-                stream <<
-                    "Failed " << pluralise( totals.testCases.failed, "test case"  ) << ", "
-                    "failed " << pluralise( totals.assertions.failed, "assertion" ) << '.';
-            }
-            else {
-                Colour colour( Colour::ResultSuccess );
-                stream <<
-                    "Passed " << bothOrAll( totals.testCases.passed )
-                              << pluralise( totals.testCases.passed, "test case"  ) <<
-                    " with "  << pluralise( totals.assertions.passed, "assertion" ) << '.';
-            }
+            m_xml.endElement();
         }
-    };
+    }
 
-    INTERNAL_CATCH_REGISTER_REPORTER( "compact", CompactReporter )
+    void XmlReporter::testCaseEnded( TestCaseStats const& testCaseStats ) {
+        StreamingReporterBase::testCaseEnded( testCaseStats );
+        XmlWriter::ScopedElement e = m_xml.scopedElement( "OverallResult" );
+        e.writeAttribute( "success", testCaseStats.totals.assertions.allOk() );
+
+        if ( m_config->showDurations() == ShowDurations::Always )
+            e.writeAttribute( "durationInSeconds", m_testCaseTimer.getElapsedSeconds() );
+
+        if( !testCaseStats.stdOut.empty() )
+            m_xml.scopedElement( "StdOut" ).writeText( trim( testCaseStats.stdOut ), XmlFormatting::Newline );
+        if( !testCaseStats.stdErr.empty() )
+            m_xml.scopedElement( "StdErr" ).writeText( trim( testCaseStats.stdErr ), XmlFormatting::Newline );
+
+        m_xml.endElement();
+    }
+
+    void XmlReporter::testGroupEnded( TestGroupStats const& testGroupStats ) {
+        StreamingReporterBase::testGroupEnded( testGroupStats );
+        // TODO: Check testGroupStats.aborting and act accordingly.
+        m_xml.scopedElement( "OverallResults" )
+            .writeAttribute( "successes", testGroupStats.totals.assertions.passed )
+            .writeAttribute( "failures", testGroupStats.totals.assertions.failed )
+            .writeAttribute( "expectedFailures", testGroupStats.totals.assertions.failedButOk );
+        m_xml.scopedElement( "OverallResultsCases")
+            .writeAttribute( "successes", testGroupStats.totals.testCases.passed )
+            .writeAttribute( "failures", testGroupStats.totals.testCases.failed )
+            .writeAttribute( "expectedFailures", testGroupStats.totals.testCases.failedButOk );
+        m_xml.endElement();
+    }
+
+    void XmlReporter::testRunEnded( TestRunStats const& testRunStats ) {
+        StreamingReporterBase::testRunEnded( testRunStats );
+        m_xml.scopedElement( "OverallResults" )
+            .writeAttribute( "successes", testRunStats.totals.assertions.passed )
+            .writeAttribute( "failures", testRunStats.totals.assertions.failed )
+            .writeAttribute( "expectedFailures", testRunStats.totals.assertions.failedButOk );
+        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
 
-namespace Catch {
-    // These are all here to avoid warnings about not having any out of line
-    // virtual methods
-    NonCopyable::~NonCopyable() {}
-    IShared::~IShared() {}
-    IStream::~IStream() CATCH_NOEXCEPT {}
-    FileStream::~FileStream() CATCH_NOEXCEPT {}
-    CoutStream::~CoutStream() CATCH_NOEXCEPT {}
-    DebugOutStream::~DebugOutStream() CATCH_NOEXCEPT {}
-    StreamBufBase::~StreamBufBase() CATCH_NOEXCEPT {}
-    IContext::~IContext() {}
-    IResultCapture::~IResultCapture() {}
-    ITestCase::~ITestCase() {}
-    ITestCaseRegistry::~ITestCaseRegistry() {}
-    IRegistryHub::~IRegistryHub() {}
-    IMutableRegistryHub::~IMutableRegistryHub() {}
-    IExceptionTranslator::~IExceptionTranslator() {}
-    IExceptionTranslatorRegistry::~IExceptionTranslatorRegistry() {}
-    IReporter::~IReporter() {}
-    IReporterFactory::~IReporterFactory() {}
-    IReporterRegistry::~IReporterRegistry() {}
-    IStreamingReporter::~IStreamingReporter() {}
-    AssertionStats::~AssertionStats() {}
-    SectionStats::~SectionStats() {}
-    TestCaseStats::~TestCaseStats() {}
-    TestGroupStats::~TestGroupStats() {}
-    TestRunStats::~TestRunStats() {}
-    CumulativeReporterBase::SectionNode::~SectionNode() {}
-    CumulativeReporterBase::~CumulativeReporterBase() {}
-
-    StreamingReporterBase::~StreamingReporterBase() {}
-    ConsoleReporter::~ConsoleReporter() {}
-    CompactReporter::~CompactReporter() {}
-    IRunner::~IRunner() {}
-    IMutableContext::~IMutableContext() {}
-    IConfig::~IConfig() {}
-    XmlReporter::~XmlReporter() {}
-    JunitReporter::~JunitReporter() {}
-    TestRegistry::~TestRegistry() {}
-    FreeFunctionTestCase::~FreeFunctionTestCase() {}
-    IGeneratorInfo::~IGeneratorInfo() {}
-    IGeneratorsForTest::~IGeneratorsForTest() {}
-    WildcardPattern::~WildcardPattern() {}
-    TestSpec::Pattern::~Pattern() {}
-    TestSpec::NamePattern::~NamePattern() {}
-    TestSpec::TagPattern::~TagPattern() {}
-    TestSpec::ExcludedPattern::~ExcludedPattern() {}
-    Matchers::Impl::MatcherUntypedBase::~MatcherUntypedBase() {}
-
-    void Config::dummy() {}
+#if defined(_MSC_VER)
+#pragma warning(pop)
+#endif
+// end catch_reporter_xml.cpp
 
-    namespace TestCaseTracking {
-        ITracker::~ITracker() {}
-        TrackerBase::~TrackerBase() {}
-        SectionTracker::~SectionTracker() {}
-        IndexTracker::~IndexTracker() {}
-    }
+namespace Catch {
+    LeakDetector leakDetector;
 }
 
 #ifdef __clang__
 #pragma clang diagnostic pop
 #endif
 
+// end catch_impl.hpp
 #endif
 
 #ifdef CATCH_CONFIG_MAIN
-// #included from: internal/catch_default_main.hpp
-#define TWOBLUECUBES_CATCH_DEFAULT_MAIN_HPP_INCLUDED
+// start catch_default_main.hpp
 
 #ifndef __OBJC__
 
-#if defined(WIN32) && defined(_UNICODE) && !defined(DO_NOT_USE_WMAIN)
+#ifndef CATCH_INTERNAL_CDECL
+#ifdef _MSC_VER
+#define CATCH_INTERNAL_CDECL __cdecl
+#else
+#define CATCH_INTERNAL_CDECL
+#endif
+#endif
+
+#if defined(CATCH_CONFIG_WCHAR) && defined(CATCH_PLATFORM_WINDOWS) && defined(_UNICODE) && !defined(DO_NOT_USE_WMAIN)
 // Standard C/C++ Win32 Unicode wmain entry point
-extern "C" int wmain (int argc, wchar_t * argv[], wchar_t * []) {
+extern "C" int CATCH_INTERNAL_CDECL wmain (int argc, wchar_t * argv[], wchar_t * []) {
 #else
 // Standard C/C++ main entry point
-int main (int argc, char * argv[]) {
+int CATCH_INTERNAL_CDECL main (int argc, char * argv[]) {
 #endif
 
-    int result = Catch::Session().run( argc, argv );
-    return ( result < 0xff ? result : 0xff );
+    return Catch::Session().run( argc, argv );
 }
 
 #else // __OBJC__
@@ -11483,197 +17552,413 @@ int main (int argc, char * const argv[]) {
 #endif
 
     Catch::registerTestMethods();
-    int result = Catch::Session().run( argc, (char* const*)argv );
+    int result = Catch::Session().run( argc, (char**)argv );
 
 #if !CATCH_ARC_ENABLED
     [pool drain];
 #endif
 
-    return ( result < 0xff ? result : 0xff );
+    return result;
 }
 
 #endif // __OBJC__
 
+// end catch_default_main.hpp
 #endif
 
+#if !defined(CATCH_CONFIG_IMPL_ONLY)
+
 #ifdef CLARA_CONFIG_MAIN_NOT_DEFINED
 #  undef CLARA_CONFIG_MAIN
 #endif
 
+#if !defined(CATCH_CONFIG_DISABLE)
 //////
-
 // If this config identifier is defined then all CATCH macros are prefixed with CATCH_
 #ifdef CATCH_CONFIG_PREFIX_ALL
 
-#if defined(CATCH_CONFIG_FAST_COMPILE)
-#define CATCH_REQUIRE( expr ) INTERNAL_CATCH_TEST_NO_TRY( "CATCH_REQUIRE", Catch::ResultDisposition::Normal, expr )
-#define CATCH_REQUIRE_FALSE( expr ) INTERNAL_CATCH_TEST_NO_TRY( "CATCH_REQUIRE_FALSE", Catch::ResultDisposition::Normal | Catch::ResultDisposition::FalseTest, expr )
-#else
-#define CATCH_REQUIRE( expr ) INTERNAL_CATCH_TEST( "CATCH_REQUIRE", Catch::ResultDisposition::Normal, expr )
-#define CATCH_REQUIRE_FALSE( expr ) INTERNAL_CATCH_TEST( "CATCH_REQUIRE_FALSE", Catch::ResultDisposition::Normal | Catch::ResultDisposition::FalseTest, expr  )
-#endif
+#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( expr ) INTERNAL_CATCH_THROWS( "CATCH_REQUIRE_THROWS", Catch::ResultDisposition::Normal, "", expr )
+#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( "CATCH_REQUIRE_THROWS_WITH", Catch::ResultDisposition::Normal, matcher, expr )
-#define CATCH_REQUIRE_NOTHROW( expr ) INTERNAL_CATCH_NO_THROW( "CATCH_REQUIRE_NOTHROW", Catch::ResultDisposition::Normal, expr )
-
-#define CATCH_CHECK( expr ) INTERNAL_CATCH_TEST( "CATCH_CHECK", Catch::ResultDisposition::ContinueOnFailure, expr )
-#define CATCH_CHECK_FALSE( expr ) INTERNAL_CATCH_TEST( "CATCH_CHECK_FALSE", Catch::ResultDisposition::ContinueOnFailure | Catch::ResultDisposition::FalseTest, expr )
-#define CATCH_CHECKED_IF( expr ) INTERNAL_CATCH_IF( "CATCH_CHECKED_IF", Catch::ResultDisposition::ContinueOnFailure, expr )
-#define CATCH_CHECKED_ELSE( expr ) INTERNAL_CATCH_ELSE( "CATCH_CHECKED_ELSE", Catch::ResultDisposition::ContinueOnFailure, expr )
-#define CATCH_CHECK_NOFAIL( expr ) INTERNAL_CATCH_TEST( "CATCH_CHECK_NOFAIL", Catch::ResultDisposition::ContinueOnFailure | Catch::ResultDisposition::SuppressFail, expr )
-
-#define CATCH_CHECK_THROWS( expr )  INTERNAL_CATCH_THROWS( "CATCH_CHECK_THROWS", Catch::ResultDisposition::ContinueOnFailure, "", 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)
+#define CATCH_REQUIRE_THROWS_MATCHES( expr, exceptionType, matcher ) INTERNAL_CATCH_THROWS_MATCHES( "CATCH_REQUIRE_THROWS_MATCHES", exceptionType, Catch::ResultDisposition::Normal, matcher, expr )
+#endif// CATCH_CONFIG_DISABLE_MATCHERS
+#define CATCH_REQUIRE_NOTHROW( ... ) INTERNAL_CATCH_NO_THROW( "CATCH_REQUIRE_NOTHROW", Catch::ResultDisposition::Normal, __VA_ARGS__ )
+
+#define CATCH_CHECK( ... ) INTERNAL_CATCH_TEST( "CATCH_CHECK", Catch::ResultDisposition::ContinueOnFailure, __VA_ARGS__ )
+#define CATCH_CHECK_FALSE( ... ) INTERNAL_CATCH_TEST( "CATCH_CHECK_FALSE", Catch::ResultDisposition::ContinueOnFailure | Catch::ResultDisposition::FalseTest, __VA_ARGS__ )
+#define CATCH_CHECKED_IF( ... ) INTERNAL_CATCH_IF( "CATCH_CHECKED_IF", Catch::ResultDisposition::ContinueOnFailure, __VA_ARGS__ )
+#define CATCH_CHECKED_ELSE( ... ) INTERNAL_CATCH_ELSE( "CATCH_CHECKED_ELSE", Catch::ResultDisposition::ContinueOnFailure, __VA_ARGS__ )
+#define CATCH_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_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( "CATCH_CHECK_THROWS_WITH", Catch::ResultDisposition::ContinueOnFailure, matcher, expr )
-#define CATCH_CHECK_NOTHROW( expr ) INTERNAL_CATCH_NO_THROW( "CATCH_CHECK_NOTHROW", 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)
+#define CATCH_CHECK_THROWS_MATCHES( expr, exceptionType, matcher ) INTERNAL_CATCH_THROWS_MATCHES( "CATCH_CHECK_THROWS_MATCHES", exceptionType, Catch::ResultDisposition::ContinueOnFailure, matcher, expr )
+#endif // CATCH_CONFIG_DISABLE_MATCHERS
+#define CATCH_CHECK_NOTHROW( ... ) INTERNAL_CATCH_NO_THROW( "CATCH_CHECK_NOTHROW", Catch::ResultDisposition::ContinueOnFailure, __VA_ARGS__ )
 
+#if !defined(CATCH_CONFIG_DISABLE_MATCHERS)
 #define CATCH_CHECK_THAT( arg, matcher ) INTERNAL_CHECK_THAT( "CATCH_CHECK_THAT", matcher, Catch::ResultDisposition::ContinueOnFailure, arg )
 
-#if defined(CATCH_CONFIG_FAST_COMPILE)
-#define CATCH_REQUIRE_THAT( arg, matcher ) INTERNAL_CHECK_THAT_NO_TRY( "CATCH_REQUIRE_THAT", matcher, Catch::ResultDisposition::Normal, arg )
-#else
 #define CATCH_REQUIRE_THAT( arg, matcher ) INTERNAL_CHECK_THAT( "CATCH_REQUIRE_THAT", matcher, Catch::ResultDisposition::Normal, arg )
-#endif
+#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_SCOPED_INFO( msg ) INTERNAL_CATCH_INFO( "CATCH_INFO", msg )
-#define CATCH_CAPTURE( msg ) INTERNAL_CATCH_INFO( "CATCH_CAPTURE", #msg " := " << Catch::toString(msg) )
-#define CATCH_SCOPED_CAPTURE( msg ) INTERNAL_CATCH_INFO( "CATCH_CAPTURE", #msg " := " << Catch::toString(msg) )
-
-#ifdef CATCH_CONFIG_VARIADIC_MACROS
-    #define CATCH_TEST_CASE( ... ) INTERNAL_CATCH_TESTCASE( __VA_ARGS__ )
-    #define CATCH_TEST_CASE_METHOD( className, ... ) INTERNAL_CATCH_TEST_CASE_METHOD( className, __VA_ARGS__ )
-    #define CATCH_METHOD_AS_TEST_CASE( method, ... ) INTERNAL_CATCH_METHOD_AS_TEST_CASE( method, __VA_ARGS__ )
-    #define CATCH_REGISTER_TEST_CASE( Function, ... ) INTERNAL_CATCH_REGISTER_TESTCASE( Function, __VA_ARGS__ )
-    #define CATCH_SECTION( ... ) INTERNAL_CATCH_SECTION( __VA_ARGS__ )
-    #define CATCH_FAIL( ... ) INTERNAL_CATCH_MSG( "CATCH_FAIL", Catch::ResultWas::ExplicitFailure, Catch::ResultDisposition::Normal, __VA_ARGS__ )
-    #define CATCH_FAIL_CHECK( ... ) INTERNAL_CATCH_MSG( "CATCH_FAIL_CHECK", Catch::ResultWas::ExplicitFailure, Catch::ResultDisposition::ContinueOnFailure, __VA_ARGS__ )
-    #define CATCH_SUCCEED( ... ) INTERNAL_CATCH_MSG( "CATCH_SUCCEED", Catch::ResultWas::Ok, Catch::ResultDisposition::ContinueOnFailure, __VA_ARGS__ )
+#define CATCH_CAPTURE( ... ) INTERNAL_CATCH_CAPTURE( INTERNAL_CATCH_UNIQUE_NAME(capturer), "CATCH_CAPTURE",__VA_ARGS__ )
+
+#define CATCH_TEST_CASE( ... ) INTERNAL_CATCH_TESTCASE( __VA_ARGS__ )
+#define CATCH_TEST_CASE_METHOD( className, ... ) INTERNAL_CATCH_TEST_CASE_METHOD( className, __VA_ARGS__ )
+#define CATCH_METHOD_AS_TEST_CASE( method, ... ) INTERNAL_CATCH_METHOD_AS_TEST_CASE( method, __VA_ARGS__ )
+#define CATCH_REGISTER_TEST_CASE( Function, ... ) INTERNAL_CATCH_REGISTER_TESTCASE( Function, __VA_ARGS__ )
+#define CATCH_SECTION( ... ) INTERNAL_CATCH_SECTION( __VA_ARGS__ )
+#define CATCH_DYNAMIC_SECTION( ... ) INTERNAL_CATCH_DYNAMIC_SECTION( __VA_ARGS__ )
+#define CATCH_FAIL( ... ) INTERNAL_CATCH_MSG( "CATCH_FAIL", Catch::ResultWas::ExplicitFailure, Catch::ResultDisposition::Normal, __VA_ARGS__ )
+#define CATCH_FAIL_CHECK( ... ) INTERNAL_CATCH_MSG( "CATCH_FAIL_CHECK", Catch::ResultWas::ExplicitFailure, Catch::ResultDisposition::ContinueOnFailure, __VA_ARGS__ )
+#define CATCH_SUCCEED( ... ) INTERNAL_CATCH_MSG( "CATCH_SUCCEED", Catch::ResultWas::Ok, Catch::ResultDisposition::ContinueOnFailure, __VA_ARGS__ )
+
+#define CATCH_ANON_TEST_CASE() INTERNAL_CATCH_TESTCASE()
+
+#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_TEST_CASE( name, description ) INTERNAL_CATCH_TESTCASE( name, description )
-    #define CATCH_TEST_CASE_METHOD( className, name, description ) INTERNAL_CATCH_TEST_CASE_METHOD( className, name, description )
-    #define CATCH_METHOD_AS_TEST_CASE( method, name, description ) INTERNAL_CATCH_METHOD_AS_TEST_CASE( method, name, description )
-    #define CATCH_REGISTER_TEST_CASE( function, name, description ) INTERNAL_CATCH_REGISTER_TESTCASE( function, name, description )
-    #define CATCH_SECTION( name, description ) INTERNAL_CATCH_SECTION( name, description )
-    #define CATCH_FAIL( msg ) INTERNAL_CATCH_MSG( "CATCH_FAIL", Catch::ResultWas::ExplicitFailure, Catch::ResultDisposition::Normal, msg )
-    #define CATCH_FAIL_CHECK( msg ) INTERNAL_CATCH_MSG( "CATCH_FAIL_CHECK", Catch::ResultWas::ExplicitFailure, Catch::ResultDisposition::ContinueOnFailure, msg )
-    #define CATCH_SUCCEED( msg ) INTERNAL_CATCH_MSG( "CATCH_SUCCEED", Catch::ResultWas::Ok, Catch::ResultDisposition::ContinueOnFailure, msg )
+#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
-#define CATCH_ANON_TEST_CASE() INTERNAL_CATCH_TESTCASE( "", "" )
-
-#define CATCH_REGISTER_REPORTER( name, reporterType ) INTERNAL_CATCH_REGISTER_REPORTER( name, reporterType )
-#define CATCH_REGISTER_LEGACY_REPORTER( name, reporterType ) INTERNAL_CATCH_REGISTER_LEGACY_REPORTER( name, reporterType )
 
-#define CATCH_GENERATE( expr) INTERNAL_CATCH_GENERATE( expr )
+#if !defined(CATCH_CONFIG_RUNTIME_STATIC_REQUIRE)
+#define CATCH_STATIC_REQUIRE( ... )       static_assert(   __VA_ARGS__ ,      #__VA_ARGS__ );     CATCH_SUCCEED( #__VA_ARGS__ )
+#define CATCH_STATIC_REQUIRE_FALSE( ... ) static_assert( !(__VA_ARGS__), "!(" #__VA_ARGS__ ")" ); CATCH_SUCCEED( #__VA_ARGS__ )
+#else
+#define CATCH_STATIC_REQUIRE( ... )       CATCH_REQUIRE( __VA_ARGS__ )
+#define CATCH_STATIC_REQUIRE_FALSE( ... ) CATCH_REQUIRE_FALSE( __VA_ARGS__ )
+#endif
 
 // "BDD-style" convenience wrappers
-#ifdef CATCH_CONFIG_VARIADIC_MACROS
 #define CATCH_SCENARIO( ... ) CATCH_TEST_CASE( "Scenario: " __VA_ARGS__ )
 #define CATCH_SCENARIO_METHOD( className, ... ) INTERNAL_CATCH_TEST_CASE_METHOD( className, "Scenario: " __VA_ARGS__ )
-#else
-#define CATCH_SCENARIO( name, tags ) CATCH_TEST_CASE( "Scenario: " name, tags )
-#define CATCH_SCENARIO_METHOD( className, name, tags ) INTERNAL_CATCH_TEST_CASE_METHOD( className, "Scenario: " name, tags )
-#endif
-#define CATCH_GIVEN( desc )    CATCH_SECTION( std::string( "Given: ") + desc, "" )
-#define CATCH_WHEN( desc )     CATCH_SECTION( std::string( " When: ") + desc, "" )
-#define CATCH_AND_WHEN( desc ) CATCH_SECTION( std::string( "  And: ") + desc, "" )
-#define CATCH_THEN( desc )     CATCH_SECTION( std::string( " Then: ") + desc, "" )
-#define CATCH_AND_THEN( desc ) CATCH_SECTION( std::string( "  And: ") + desc, "" )
+#define CATCH_GIVEN( desc )     INTERNAL_CATCH_DYNAMIC_SECTION( "    Given: " << desc )
+#define CATCH_AND_GIVEN( desc ) INTERNAL_CATCH_DYNAMIC_SECTION( "And given: " << desc )
+#define CATCH_WHEN( desc )      INTERNAL_CATCH_DYNAMIC_SECTION( "     When: " << desc )
+#define CATCH_AND_WHEN( desc )  INTERNAL_CATCH_DYNAMIC_SECTION( " And when: " << desc )
+#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
 
-#if defined(CATCH_CONFIG_FAST_COMPILE)
-#define REQUIRE( expr ) INTERNAL_CATCH_TEST_NO_TRY( "REQUIRE", Catch::ResultDisposition::Normal, expr )
-#define REQUIRE_FALSE( expr ) INTERNAL_CATCH_TEST_NO_TRY( "REQUIRE_FALSE", Catch::ResultDisposition::Normal | Catch::ResultDisposition::FalseTest, expr )
-
-#else
-#define REQUIRE( expr ) INTERNAL_CATCH_TEST( "REQUIRE", Catch::ResultDisposition::Normal, expr  )
-#define REQUIRE_FALSE( expr ) INTERNAL_CATCH_TEST( "REQUIRE_FALSE", Catch::ResultDisposition::Normal | Catch::ResultDisposition::FalseTest, expr )
-#endif
+#define REQUIRE( ... ) INTERNAL_CATCH_TEST( "REQUIRE", Catch::ResultDisposition::Normal, __VA_ARGS__  )
+#define REQUIRE_FALSE( ... ) INTERNAL_CATCH_TEST( "REQUIRE_FALSE", Catch::ResultDisposition::Normal | Catch::ResultDisposition::FalseTest, __VA_ARGS__ )
 
-#define REQUIRE_THROWS( expr ) INTERNAL_CATCH_THROWS( "REQUIRE_THROWS", Catch::ResultDisposition::Normal, "", expr )
+#define REQUIRE_THROWS( ... ) INTERNAL_CATCH_THROWS( "REQUIRE_THROWS", Catch::ResultDisposition::Normal, __VA_ARGS__ )
 #define REQUIRE_THROWS_AS( expr, exceptionType ) INTERNAL_CATCH_THROWS_AS( "REQUIRE_THROWS_AS", exceptionType, Catch::ResultDisposition::Normal, expr )
-#define REQUIRE_THROWS_WITH( expr, matcher ) INTERNAL_CATCH_THROWS( "REQUIRE_THROWS_WITH", Catch::ResultDisposition::Normal, matcher, expr )
-#define REQUIRE_NOTHROW( expr ) INTERNAL_CATCH_NO_THROW( "REQUIRE_NOTHROW", Catch::ResultDisposition::Normal, expr )
-
-#define CHECK( expr ) INTERNAL_CATCH_TEST( "CHECK", Catch::ResultDisposition::ContinueOnFailure, expr )
-#define CHECK_FALSE( expr ) INTERNAL_CATCH_TEST( "CHECK_FALSE", Catch::ResultDisposition::ContinueOnFailure | Catch::ResultDisposition::FalseTest, expr )
-#define CHECKED_IF( expr ) INTERNAL_CATCH_IF( "CHECKED_IF", Catch::ResultDisposition::ContinueOnFailure, expr )
-#define CHECKED_ELSE( expr ) INTERNAL_CATCH_ELSE( "CHECKED_ELSE", Catch::ResultDisposition::ContinueOnFailure, expr )
-#define CHECK_NOFAIL( expr ) INTERNAL_CATCH_TEST( "CHECK_NOFAIL", Catch::ResultDisposition::ContinueOnFailure | Catch::ResultDisposition::SuppressFail, expr )
-
-#define CHECK_THROWS( expr )  INTERNAL_CATCH_THROWS( "CHECK_THROWS", Catch::ResultDisposition::ContinueOnFailure, "", expr )
+#define REQUIRE_THROWS_WITH( expr, matcher ) INTERNAL_CATCH_THROWS_STR_MATCHES( "REQUIRE_THROWS_WITH", Catch::ResultDisposition::Normal, matcher, expr )
+#if !defined(CATCH_CONFIG_DISABLE_MATCHERS)
+#define REQUIRE_THROWS_MATCHES( expr, exceptionType, matcher ) INTERNAL_CATCH_THROWS_MATCHES( "REQUIRE_THROWS_MATCHES", exceptionType, Catch::ResultDisposition::Normal, matcher, expr )
+#endif // CATCH_CONFIG_DISABLE_MATCHERS
+#define REQUIRE_NOTHROW( ... ) INTERNAL_CATCH_NO_THROW( "REQUIRE_NOTHROW", Catch::ResultDisposition::Normal, __VA_ARGS__ )
+
+#define CHECK( ... ) INTERNAL_CATCH_TEST( "CHECK", Catch::ResultDisposition::ContinueOnFailure, __VA_ARGS__ )
+#define CHECK_FALSE( ... ) INTERNAL_CATCH_TEST( "CHECK_FALSE", Catch::ResultDisposition::ContinueOnFailure | Catch::ResultDisposition::FalseTest, __VA_ARGS__ )
+#define CHECKED_IF( ... ) INTERNAL_CATCH_IF( "CHECKED_IF", Catch::ResultDisposition::ContinueOnFailure, __VA_ARGS__ )
+#define CHECKED_ELSE( ... ) INTERNAL_CATCH_ELSE( "CHECKED_ELSE", Catch::ResultDisposition::ContinueOnFailure, __VA_ARGS__ )
+#define CHECK_NOFAIL( ... ) INTERNAL_CATCH_TEST( "CHECK_NOFAIL", Catch::ResultDisposition::ContinueOnFailure | Catch::ResultDisposition::SuppressFail, __VA_ARGS__ )
+
+#define CHECK_THROWS( ... )  INTERNAL_CATCH_THROWS( "CHECK_THROWS", Catch::ResultDisposition::ContinueOnFailure, __VA_ARGS__ )
 #define CHECK_THROWS_AS( expr, exceptionType ) INTERNAL_CATCH_THROWS_AS( "CHECK_THROWS_AS", exceptionType, Catch::ResultDisposition::ContinueOnFailure, expr )
-#define CHECK_THROWS_WITH( expr, matcher ) INTERNAL_CATCH_THROWS( "CHECK_THROWS_WITH", Catch::ResultDisposition::ContinueOnFailure, matcher, expr )
-#define CHECK_NOTHROW( expr ) INTERNAL_CATCH_NO_THROW( "CHECK_NOTHROW", Catch::ResultDisposition::ContinueOnFailure, expr )
+#define CHECK_THROWS_WITH( expr, matcher ) INTERNAL_CATCH_THROWS_STR_MATCHES( "CHECK_THROWS_WITH", Catch::ResultDisposition::ContinueOnFailure, matcher, expr )
+#if !defined(CATCH_CONFIG_DISABLE_MATCHERS)
+#define CHECK_THROWS_MATCHES( expr, exceptionType, matcher ) INTERNAL_CATCH_THROWS_MATCHES( "CHECK_THROWS_MATCHES", exceptionType, Catch::ResultDisposition::ContinueOnFailure, matcher, expr )
+#endif // CATCH_CONFIG_DISABLE_MATCHERS
+#define CHECK_NOTHROW( ... ) INTERNAL_CATCH_NO_THROW( "CHECK_NOTHROW", Catch::ResultDisposition::ContinueOnFailure, __VA_ARGS__ )
 
+#if !defined(CATCH_CONFIG_DISABLE_MATCHERS)
 #define CHECK_THAT( arg, matcher ) INTERNAL_CHECK_THAT( "CHECK_THAT", matcher, Catch::ResultDisposition::ContinueOnFailure, arg )
 
-#if defined(CATCH_CONFIG_FAST_COMPILE)
-#define REQUIRE_THAT( arg, matcher ) INTERNAL_CHECK_THAT_NO_TRY( "REQUIRE_THAT", matcher, Catch::ResultDisposition::Normal, arg )
-#else
 #define REQUIRE_THAT( arg, matcher ) INTERNAL_CHECK_THAT( "REQUIRE_THAT", matcher, Catch::ResultDisposition::Normal, arg )
-#endif
+#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 SCOPED_INFO( msg ) INTERNAL_CATCH_INFO( "INFO", msg )
-#define CAPTURE( msg ) INTERNAL_CATCH_INFO( "CAPTURE", #msg " := " << Catch::toString(msg) )
-#define SCOPED_CAPTURE( msg ) INTERNAL_CATCH_INFO( "CAPTURE", #msg " := " << Catch::toString(msg) )
+#define CAPTURE( ... ) INTERNAL_CATCH_CAPTURE( INTERNAL_CATCH_UNIQUE_NAME(capturer), "CAPTURE",__VA_ARGS__ )
 
-#ifdef CATCH_CONFIG_VARIADIC_MACROS
 #define TEST_CASE( ... ) INTERNAL_CATCH_TESTCASE( __VA_ARGS__ )
 #define TEST_CASE_METHOD( className, ... ) INTERNAL_CATCH_TEST_CASE_METHOD( className, __VA_ARGS__ )
 #define METHOD_AS_TEST_CASE( method, ... ) INTERNAL_CATCH_METHOD_AS_TEST_CASE( method, __VA_ARGS__ )
 #define REGISTER_TEST_CASE( Function, ... ) INTERNAL_CATCH_REGISTER_TESTCASE( Function, __VA_ARGS__ )
 #define SECTION( ... ) INTERNAL_CATCH_SECTION( __VA_ARGS__ )
+#define DYNAMIC_SECTION( ... ) INTERNAL_CATCH_DYNAMIC_SECTION( __VA_ARGS__ )
 #define FAIL( ... ) INTERNAL_CATCH_MSG( "FAIL", Catch::ResultWas::ExplicitFailure, Catch::ResultDisposition::Normal, __VA_ARGS__ )
 #define FAIL_CHECK( ... ) INTERNAL_CATCH_MSG( "FAIL_CHECK", Catch::ResultWas::ExplicitFailure, Catch::ResultDisposition::ContinueOnFailure, __VA_ARGS__ )
 #define SUCCEED( ... ) INTERNAL_CATCH_MSG( "SUCCEED", Catch::ResultWas::Ok, Catch::ResultDisposition::ContinueOnFailure, __VA_ARGS__ )
+#define ANON_TEST_CASE() INTERNAL_CATCH_TESTCASE()
+
+#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 TEST_CASE( name, description ) INTERNAL_CATCH_TESTCASE( name, description )
-    #define TEST_CASE_METHOD( className, name, description ) INTERNAL_CATCH_TEST_CASE_METHOD( className, name, description )
-    #define METHOD_AS_TEST_CASE( method, name, description ) INTERNAL_CATCH_METHOD_AS_TEST_CASE( method, name, description )
-    #define REGISTER_TEST_CASE( method, name, description ) INTERNAL_CATCH_REGISTER_TESTCASE( method, name, description )
-    #define SECTION( name, description ) INTERNAL_CATCH_SECTION( name, description )
-    #define FAIL( msg ) INTERNAL_CATCH_MSG( "FAIL", Catch::ResultWas::ExplicitFailure, Catch::ResultDisposition::Normal, msg )
-    #define FAIL_CHECK( msg ) INTERNAL_CATCH_MSG( "FAIL_CHECK", Catch::ResultWas::ExplicitFailure, Catch::ResultDisposition::ContinueOnFailure, msg )
-    #define SUCCEED( msg ) INTERNAL_CATCH_MSG( "SUCCEED", Catch::ResultWas::Ok, Catch::ResultDisposition::ContinueOnFailure, msg )
+#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
-#define ANON_TEST_CASE() INTERNAL_CATCH_TESTCASE( "", "" )
-
-#define REGISTER_REPORTER( name, reporterType ) INTERNAL_CATCH_REGISTER_REPORTER( name, reporterType )
-#define REGISTER_LEGACY_REPORTER( name, reporterType ) INTERNAL_CATCH_REGISTER_LEGACY_REPORTER( name, reporterType )
 
-#define GENERATE( expr) INTERNAL_CATCH_GENERATE( expr )
+#if !defined(CATCH_CONFIG_RUNTIME_STATIC_REQUIRE)
+#define STATIC_REQUIRE( ... )       static_assert(   __VA_ARGS__,  #__VA_ARGS__ ); SUCCEED( #__VA_ARGS__ )
+#define STATIC_REQUIRE_FALSE( ... ) static_assert( !(__VA_ARGS__), "!(" #__VA_ARGS__ ")" ); SUCCEED( "!(" #__VA_ARGS__ ")" )
+#else
+#define STATIC_REQUIRE( ... )       REQUIRE( __VA_ARGS__ )
+#define STATIC_REQUIRE_FALSE( ... ) REQUIRE_FALSE( __VA_ARGS__ )
+#endif
 
 #endif
 
 #define CATCH_TRANSLATE_EXCEPTION( signature ) INTERNAL_CATCH_TRANSLATE_EXCEPTION( signature )
 
 // "BDD-style" convenience wrappers
-#ifdef CATCH_CONFIG_VARIADIC_MACROS
 #define SCENARIO( ... ) TEST_CASE( "Scenario: " __VA_ARGS__ )
 #define SCENARIO_METHOD( className, ... ) INTERNAL_CATCH_TEST_CASE_METHOD( className, "Scenario: " __VA_ARGS__ )
+
+#define GIVEN( desc )     INTERNAL_CATCH_DYNAMIC_SECTION( "    Given: " << desc )
+#define AND_GIVEN( desc ) INTERNAL_CATCH_DYNAMIC_SECTION( "And given: " << desc )
+#define WHEN( desc )      INTERNAL_CATCH_DYNAMIC_SECTION( "     When: " << desc )
+#define AND_WHEN( desc )  INTERNAL_CATCH_DYNAMIC_SECTION( " And when: " << desc )
+#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
+
+//////
+// If this config identifier is defined then all CATCH macros are prefixed with CATCH_
+#ifdef CATCH_CONFIG_PREFIX_ALL
+
+#define CATCH_REQUIRE( ... )        (void)(0)
+#define CATCH_REQUIRE_FALSE( ... )  (void)(0)
+
+#define CATCH_REQUIRE_THROWS( ... ) (void)(0)
+#define CATCH_REQUIRE_THROWS_AS( expr, exceptionType ) (void)(0)
+#define CATCH_REQUIRE_THROWS_WITH( expr, matcher )     (void)(0)
+#if !defined(CATCH_CONFIG_DISABLE_MATCHERS)
+#define CATCH_REQUIRE_THROWS_MATCHES( expr, exceptionType, matcher ) (void)(0)
+#endif// CATCH_CONFIG_DISABLE_MATCHERS
+#define CATCH_REQUIRE_NOTHROW( ... ) (void)(0)
+
+#define CATCH_CHECK( ... )         (void)(0)
+#define CATCH_CHECK_FALSE( ... )   (void)(0)
+#define CATCH_CHECKED_IF( ... )    if (__VA_ARGS__)
+#define CATCH_CHECKED_ELSE( ... )  if (!(__VA_ARGS__))
+#define CATCH_CHECK_NOFAIL( ... )  (void)(0)
+
+#define CATCH_CHECK_THROWS( ... )  (void)(0)
+#define CATCH_CHECK_THROWS_AS( expr, exceptionType ) (void)(0)
+#define CATCH_CHECK_THROWS_WITH( expr, matcher )     (void)(0)
+#if !defined(CATCH_CONFIG_DISABLE_MATCHERS)
+#define CATCH_CHECK_THROWS_MATCHES( expr, exceptionType, matcher ) (void)(0)
+#endif // CATCH_CONFIG_DISABLE_MATCHERS
+#define CATCH_CHECK_NOTHROW( ... ) (void)(0)
+
+#if !defined(CATCH_CONFIG_DISABLE_MATCHERS)
+#define CATCH_CHECK_THAT( arg, matcher )   (void)(0)
+
+#define CATCH_REQUIRE_THAT( arg, matcher ) (void)(0)
+#endif // CATCH_CONFIG_DISABLE_MATCHERS
+
+#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_METHOD_AS_TEST_CASE( method, ... )
+#define CATCH_REGISTER_TEST_CASE( Function, ... ) (void)(0)
+#define CATCH_SECTION( ... )
+#define CATCH_DYNAMIC_SECTION( ... )
+#define CATCH_FAIL( ... ) (void)(0)
+#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_ ))
+
+#ifndef CATCH_CONFIG_TRADITIONAL_MSVC_PREPROCESSOR
+#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(__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_GIVEN( desc )
+#define CATCH_AND_GIVEN( desc )
+#define CATCH_WHEN( desc )
+#define CATCH_AND_WHEN( desc )
+#define CATCH_THEN( desc )
+#define CATCH_AND_THEN( desc )
+
+#define CATCH_STATIC_REQUIRE( ... )       (void)(0)
+#define CATCH_STATIC_REQUIRE_FALSE( ... ) (void)(0)
+
+// If CATCH_CONFIG_PREFIX_ALL is not defined then the CATCH_ prefix is not required
 #else
-#define SCENARIO( name, tags ) TEST_CASE( "Scenario: " name, tags )
-#define SCENARIO_METHOD( className, name, tags ) INTERNAL_CATCH_TEST_CASE_METHOD( className, "Scenario: " name, tags )
+
+#define REQUIRE( ... )       (void)(0)
+#define REQUIRE_FALSE( ... ) (void)(0)
+
+#define REQUIRE_THROWS( ... ) (void)(0)
+#define REQUIRE_THROWS_AS( expr, exceptionType ) (void)(0)
+#define REQUIRE_THROWS_WITH( expr, matcher ) (void)(0)
+#if !defined(CATCH_CONFIG_DISABLE_MATCHERS)
+#define REQUIRE_THROWS_MATCHES( expr, exceptionType, matcher ) (void)(0)
+#endif // CATCH_CONFIG_DISABLE_MATCHERS
+#define REQUIRE_NOTHROW( ... ) (void)(0)
+
+#define CHECK( ... ) (void)(0)
+#define CHECK_FALSE( ... ) (void)(0)
+#define CHECKED_IF( ... ) if (__VA_ARGS__)
+#define CHECKED_ELSE( ... ) if (!(__VA_ARGS__))
+#define CHECK_NOFAIL( ... ) (void)(0)
+
+#define CHECK_THROWS( ... )  (void)(0)
+#define CHECK_THROWS_AS( expr, exceptionType ) (void)(0)
+#define CHECK_THROWS_WITH( expr, matcher ) (void)(0)
+#if !defined(CATCH_CONFIG_DISABLE_MATCHERS)
+#define CHECK_THROWS_MATCHES( expr, exceptionType, matcher ) (void)(0)
+#endif // CATCH_CONFIG_DISABLE_MATCHERS
+#define CHECK_NOTHROW( ... ) (void)(0)
+
+#if !defined(CATCH_CONFIG_DISABLE_MATCHERS)
+#define CHECK_THAT( arg, matcher ) (void)(0)
+
+#define REQUIRE_THAT( arg, matcher ) (void)(0)
+#endif // CATCH_CONFIG_DISABLE_MATCHERS
+
+#define INFO( msg ) (void)(0)
+#define UNSCOPED_INFO( msg ) (void)(0)
+#define WARN( msg ) (void)(0)
+#define CAPTURE( ... ) (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 METHOD_AS_TEST_CASE( method, ... )
+#define REGISTER_TEST_CASE( Function, ... ) (void)(0)
+#define SECTION( ... )
+#define DYNAMIC_SECTION( ... )
+#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_ ))
+
+#ifndef CATCH_CONFIG_TRADITIONAL_MSVC_PREPROCESSOR
+#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(__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)
+#define STATIC_REQUIRE_FALSE( ... ) (void)(0)
+
 #endif
-#define GIVEN( desc )    SECTION( std::string("   Given: ") + desc, "" )
-#define WHEN( desc )     SECTION( std::string("    When: ") + desc, "" )
-#define AND_WHEN( desc ) SECTION( std::string("And when: ") + desc, "" )
-#define THEN( desc )     SECTION( std::string("    Then: ") + desc, "" )
-#define AND_THEN( desc ) SECTION( std::string("     And: ") + desc, "" )
+
+#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 GIVEN( desc )
+#define AND_GIVEN( desc )
+#define WHEN( desc )
+#define AND_WHEN( desc )
+#define THEN( desc )
+#define AND_THEN( desc )
 
 using Catch::Detail::Approx;
 
-// #included from: internal/catch_reenable_warnings.h
+#endif
+
+#endif // ! CATCH_CONFIG_IMPL_ONLY
+
+// start catch_reenable_warnings.h
 
-#define TWOBLUECUBES_CATCH_REENABLE_WARNINGS_H_INCLUDED
 
 #ifdef __clang__
 #    ifdef __ICC // icpc defines the __clang__ macro
@@ -11685,5 +17970,7 @@ using Catch::Detail::Approx;
 #    pragma GCC diagnostic pop
 #endif
 
+// end catch_reenable_warnings.h
+// end catch.hpp
 #endif // TWOBLUECUBES_SINGLE_INCLUDE_CATCH_HPP_INCLUDED
 
diff --git a/neosensor/libraries/ArduinoJson/idf_component.yml b/neosensor/libraries/ArduinoJson/idf_component.yml
new file mode 100644
index 00000000..bbf7b39f
--- /dev/null
+++ b/neosensor/libraries/ArduinoJson/idf_component.yml
@@ -0,0 +1,13 @@
+version: "6.21.2"
+description: >-
+  A simple and efficient JSON library for embedded C++.
+  ArduinoJson supports ✔ serialization, ✔ deserialization, ✔ MessagePack, ✔ fixed allocation, ✔ zero-copy, ✔ streams, ✔ filtering, and more.
+  It is the most popular Arduino library on GitHub ❤❤❤❤❤.
+  Check out arduinojson.org for a comprehensive documentation.
+url: https://arduinojson.org/
+files:
+  exclude:
+    - "**/.vs/**/*"
+    - ".devcontainer/**/*"
+    - "examples/**/*"
+    - "extras/**/*"
diff --git a/neosensor/libraries/ArduinoJson/library.json b/neosensor/libraries/ArduinoJson/library.json
index 01596a96..507d5702 100644
--- a/neosensor/libraries/ArduinoJson/library.json
+++ b/neosensor/libraries/ArduinoJson/library.json
@@ -7,12 +7,13 @@
     "type": "git",
     "url": "https://github.com/bblanchon/ArduinoJson.git"
   },
-  "version": "6.20.0",
+  "version": "6.21.2",
   "authors": {
     "name": "Benoit Blanchon",
     "url": "https://blog.benoitblanchon.fr"
   },
   "exclude": [
+    ".devcontainer",
     ".github",
     "extras"
   ],
diff --git a/neosensor/libraries/ArduinoJson/library.properties b/neosensor/libraries/ArduinoJson/library.properties
index 72a5334e..414daa46 100644
--- a/neosensor/libraries/ArduinoJson/library.properties
+++ b/neosensor/libraries/ArduinoJson/library.properties
@@ -1,5 +1,5 @@
 name=ArduinoJson
-version=6.20.0
+version=6.21.2
 author=Benoit Blanchon <blog.benoitblanchon.fr>
 maintainer=Benoit Blanchon <blog.benoitblanchon.fr>
 sentence=A simple and efficient JSON library for embedded C++.
diff --git a/neosensor/libraries/ArduinoJson/logo.svg b/neosensor/libraries/ArduinoJson/logo.svg
deleted file mode 100644
index 5afb6ac7..00000000
--- a/neosensor/libraries/ArduinoJson/logo.svg
+++ /dev/null
@@ -1 +0,0 @@
-<svg width="301.19" height="238.96" viewBox="0 0 79.691 63.225" xmlns="http://www.w3.org/2000/svg"><defs><clipPath id="a"><path d="M0 638h1000V0H0z"/></clipPath></defs><g clip-path="url(#a)" transform="matrix(.1564 0 0 -.1564 -38.351 81.5)"><path d="m246.91 120.14 20.015 58.057h19.881l20.013-58.057h-15.771l-2.918 10.34h-22.4l-3.048-10.34zm22.135 21.605h15.771l-5.83 19.219c-.265.53-.53 1.457-.797 2.787-.398 1.323-.795 3.043-1.192 5.034-.398-1.461-.662-2.784-1.062-4.11-.397-1.322-.662-2.519-1.059-3.711zM311.46 120.14v40.029h13.387v-9.41c1.326 3.447 3.182 5.965 5.566 7.69 2.387 1.72 5.304 2.648 8.616 2.648.661 0 1.192-.133 1.723-.133.53 0 1.192-.135 1.722-.135l-1.325-12.853c-.928.266-1.723.533-2.517.664-.795.133-1.592.133-2.386.133-3.446 0-6.098-1.065-7.953-3.182-1.855-2.119-2.783-5.037-2.783-8.883V120.14zM390.72 181.64v-61.498h-12.989v6.494c-1.724-2.648-3.844-4.769-5.966-6.096-2.253-1.322-4.771-1.984-7.554-1.984-5.567 0-10.073 1.984-13.387 5.83-3.314 3.977-5.036 9.147-5.036 15.772 0 6.359 1.722 11.531 5.036 15.375 3.314 3.976 7.556 5.962 12.856 5.962 3.446 0 6.229-.664 8.35-1.853 2.121-1.191 3.977-3.314 5.701-6.098-.265.928-.265 1.989-.397 3.182-.135 1.193-.135 2.519-.135 3.975v20.939zm-12.459-41.482c0 3.05-.795 5.566-2.519 7.287-1.591 1.859-3.845 2.787-6.76 2.787-2.783 0-5.036-.928-6.761-2.787-1.588-1.721-2.385-4.237-2.385-7.287 0-3.182.797-5.698 2.385-7.426 1.725-1.852 3.978-2.647 6.761-2.647 2.915 0 5.169.795 6.76 2.647 1.724 1.859 2.519 4.244 2.519 7.426M442.28 160.17v-20.412c0-4.506-.265-7.953-1.061-10.072-.663-2.254-1.989-4.11-3.71-5.83-1.724-1.727-3.978-3.051-6.496-3.979-2.65-.793-5.831-1.322-9.41-1.322-3.578 0-6.626.529-9.277 1.322-2.651.928-4.771 2.252-6.628 3.979-1.724 1.587-3.048 3.576-3.712 5.83-.663 2.119-1.06 5.566-1.06 10.072v20.412h13.519v-21.336c0-3.316.532-5.836 1.591-7.293 1.195-1.457 2.916-2.252 5.434-2.252s4.375.795 5.435 2.252 1.59 3.842 1.59 7.293v21.336zM452.88 160.17h14.05v-40.029h-14.05zm-.795 12.461c0 2.119.795 3.974 2.386 5.435 1.457 1.588 3.313 2.25 5.434 2.25 2.254 0 4.108-.662 5.567-2.119 1.458-1.592 2.254-3.316 2.254-5.566 0-2.123-.796-3.977-2.254-5.57-1.592-1.457-3.447-2.25-5.567-2.25-2.121 0-3.977.793-5.434 2.25-1.591 1.593-2.386 3.447-2.386 5.57M477.8 120.14v40.029h12.856v-8.088c1.988 3.186 4.109 5.567 6.496 7.163 2.517 1.455 5.434 2.25 8.881 2.25 2.648 0 4.904-.53 6.891-1.325 1.99-.795 3.712-1.984 4.903-3.58 1.059-1.322 1.854-2.912 2.256-4.902.529-1.986.662-4.774.662-8.613V120.14h-14.05v21.473c0 3.181-.53 5.433-1.59 6.896-1.062 1.455-2.651 2.25-5.035 2.25-2.918 0-5.04-1.058-6.365-3.316-1.325-2.119-1.987-5.698-1.987-10.469V120.14zM573.23 140.03c0-2.918-.529-5.566-1.721-8.221-1.059-2.515-2.653-4.904-4.905-6.89-2.118-2.125-4.507-3.711-7.287-4.774-2.653-1.056-5.439-1.586-8.484-1.586-3.05 0-5.963.53-8.617 1.586-2.653 1.063-5.037 2.649-7.155 4.774-2.124 1.986-3.845 4.242-4.904 6.89-1.064 2.522-1.594 5.303-1.594 8.221s.53 5.697 1.594 8.35c1.059 2.515 2.648 4.904 4.904 6.892 1.986 2.117 4.37 3.578 7.155 4.639 2.654 1.058 5.567 1.588 8.617 1.588 3.045 0 5.962-.53 8.612-1.588 2.652-1.061 5.041-2.522 7.159-4.639 2.252-2.119 3.846-4.377 4.905-7.025 1.192-2.52 1.721-5.299 1.721-8.217m-14.05 0c0 3.049-.795 5.299-2.252 7.021-1.457 1.727-3.579 2.655-6.095 2.655-2.654 0-4.639-.928-6.096-2.655-1.461-1.722-2.256-3.972-2.256-7.021 0-2.918.795-5.303 2.256-7.027 1.457-1.725 3.442-2.518 6.096-2.518 2.516 0 4.638.793 6.095 2.518 1.457 1.724 2.252 4.109 2.252 7.027" fill="#00868e"/><path d="M593.77 178.2h15.776v-37.643c0-4.109-.264-7.287-.799-9.545a18.004 18.004 0 0 0-2.649-5.832c-1.589-2.115-3.58-3.843-5.964-4.902-2.387-1.191-4.903-1.721-7.821-1.721-3.712 0-7.022.793-9.808 2.383-2.914 1.588-5.301 3.846-7.289 6.889l9.676 8.75c.133-1.723.796-3.045 1.589-3.977.928-.927 1.987-1.326 3.316-1.326 1.456 0 2.516.797 3.178 2.524.529 1.586.795 5.83.795 12.455zM616.44 123.46l3.051 10.734c1.985-1.724 4.237-3.181 6.758-4.107 2.519-.928 4.903-1.459 7.424-1.459 1.854 0 3.178.268 4.105.928.933.531 1.329 1.326 1.329 2.384 0 1.86-1.858 3.182-5.571 3.979-1.192.264-2.251.529-2.913.793-4.242 1.191-7.292 2.652-9.279 4.639-1.853 1.859-2.786 4.244-2.786 7.293 0 3.839 1.462 7.021 4.375 9.277 2.917 2.385 6.895 3.576 11.8 3.576 2.648 0 5.168-.396 7.816-.928a53.904 53.904 0 0 0 8.22-2.785l-2.918-9.67c-1.853 1.192-3.841 2.25-5.698 2.912-1.986.664-3.846.928-5.831.928-1.589 0-2.786-.264-3.713-.793-.794-.533-1.192-1.193-1.192-2.119 0-1.463 1.855-2.654 5.566-3.582.928-.264 1.457-.396 1.859-.529 4.904-1.455 8.348-3.051 10.205-4.903 1.987-1.988 2.914-4.507 2.914-7.691 0-4.104-1.589-7.42-4.771-9.939-3.047-2.518-7.289-3.84-12.457-3.84-3.448 0-6.763.396-9.809 1.191-2.918.793-5.831 2.119-8.484 3.711M701.93 140.03c0-2.918-.53-5.566-1.723-8.221a20.15 20.15 0 0 0-4.77-6.89c-2.252-2.125-4.64-3.711-7.288-4.774a23.769 23.769 0 0 0-8.616-1.586c-3.051 0-5.964.53-8.618 1.586-2.647 1.063-5.036 2.649-7.155 4.774-2.123 1.986-3.712 4.242-4.903 6.89-1.065 2.522-1.594 5.303-1.594 8.221s.529 5.697 1.594 8.35c1.191 2.515 2.78 4.904 4.903 6.892 2.119 2.117 4.508 3.578 7.155 4.639 2.654 1.058 5.567 1.588 8.618 1.588 3.177 0 5.962-.53 8.616-1.588 2.781-1.061 5.169-2.522 7.288-4.639 2.119-2.119 3.713-4.377 4.904-7.025 1.059-2.52 1.589-5.299 1.589-8.217m-14.05 0c0 3.049-.663 5.299-2.252 7.021-1.457 1.727-3.447 2.655-6.095 2.655-2.521 0-4.64-.928-6.097-2.655-1.461-1.722-2.255-3.972-2.255-7.021 0-2.918.794-5.303 2.255-7.027 1.457-1.725 3.576-2.518 6.097-2.518 2.648 0 4.638.793 6.095 2.518 1.589 1.724 2.252 4.109 2.252 7.027M710.01 120.14v40.029h12.992v-8.088c1.854 3.186 3.973 5.567 6.492 7.163 2.516 1.455 5.434 2.25 8.882 2.25 2.516 0 4.904-.53 6.89-1.325s3.58-1.984 4.904-3.58c1.06-1.322 1.854-2.912 2.257-4.902.396-1.986.661-4.774.661-8.613V120.14h-14.05v21.473c0 3.181-.529 5.433-1.589 6.896-1.064 1.455-2.786 2.25-5.042 2.25-2.913 0-5.036-1.058-6.492-3.316-1.324-2.119-1.987-5.698-1.987-10.469V120.14zM703.39 417.11l-8.81-3.271c5.363-15.116 8.291-31.384 8.291-48.338 0-4.616-.226-9.177-.648-13.683l9.375-.627c.434 4.712.667 9.484.667 14.31 0 18.104-3.136 35.475-8.875 51.609M558.32 220.95c-15.705 0-30.819 2.516-44.978 7.146a153.294 153.294 0 0 0-11.51-5.49c-.144-.06-.29-.117-.434-.177 17.609-7.012 36.814-10.874 56.922-10.874 48.132 0 91.098 22.094 119.33 56.688l-8.571 4.381c-26.517-31.586-66.288-51.674-110.76-51.674" fill="#e5ad23"/><path d="M441.68 219.54c-21.463 0-41.833 4.674-60.154 13.053l-4.695-7.885c19.71-9.17 41.679-14.299 64.849-14.299 21.177 0 41.352 4.283 59.716 12.022a153.11 153.11 0 0 0-11.733 5.257c-15.018-5.273-31.164-8.148-47.983-8.148" fill="#e47128"/><path d="M552.47 271.13c.009 0 .017-.004.023-.004-.006 0-.014.004-.023.004M554.52 271.03a83.931 83.931 0 0 1 3.8-.098c1.279 0 2.549.041 3.814.098a94.767 94.767 0 0 0-3.818-.096c-1.273 0-2.534.045-3.796.096M554.31 459.97c1.311.054 2.624.099 3.947.1a84.766 84.766 0 0 1-3.947-.1M550.65 459.73h.006-.006M564.11 271.12c.021.002.04.002.061.006h.001c-.021 0-.041-.004-.062-.006M562.34 459.97c-1.334.056-2.67.101-4.018.101h-.002c1.349 0 2.686-.046 4.021-.102l-.001.001M595.63 364.35c0 33.746-10.866 64.951-29.28 90.321a90.409 90.409 0 0 1-8.033.377c-1.23 0-2.45-.044-3.667-.093 19.923-24.808 31.849-56.312 31.849-90.605 0-33.273-11.235-63.909-30.102-88.358.64-.014 1.276-.049 1.92-.049 3.298 0 6.55.191 9.755.539 17.361 24.918 27.558 55.196 27.558 87.868" fill="#e5ad23"/><path d="M672.62 365.5c0 63.125-51.175 114.3-114.3 114.3-4.692 0-9.316-.289-13.861-.839-3.962-.48-7.86-1.156-11.687-2.03a145.022 145.022 0 0 0 8.947-7.866c3.831.61 7.731 1.013 11.688 1.195 1.629.076 3.266.119 4.913.119 57.922 0 104.88-46.956 104.88-104.88 0-3.787-.206-7.525-.597-11.208l9.292-1.675c.474 4.23.727 8.526.727 12.883" fill="#e5ad23"/><path d="m383.38 235.7 2.881 4.838c-47.274 21.193-80.206 68.66-80.206 123.82 0 8.489.321 14.111 1.814 22.167l-18.228 2.079a155.144 155.144 0 0 1-1.907-24.246c0-61.852 36.484-115.18 89.1-139.65l4.695 7.885zM475.99 495.6a134.617 134.617 0 0 0 12.062-3.756 142.93 142.93 0 0 0 6.585 3.453c1.45.713 2.909 1.406 4.384 2.071a143.17 143.17 0 0 0 11.648 4.638c14.921 5.208 30.952 8.047 47.649 8.047 50.216 0 94.445-25.61 120.36-64.478l7.703 5.385c-27.61 41.291-74.657 68.488-128.06 68.488-21.178 0-41.356-4.278-59.72-12.015a153.202 153.202 0 0 1-11.628-5.493 152.38 152.38 0 0 1-4.39-2.391 141.193 141.193 0 0 1-50.108 5.718l-.323-5.623a135.848 135.848 0 0 0 43.841-4.044" fill="#62aeb2"/><path d="m342.81 347.38 4.169 2.559a96.39 96.39 0 0 0-1.083 14.418c0 51.248 40.26 93.047 90.872 95.611a154.602 154.602 0 0 0 3.806 4.693 155.257 155.257 0 0 0 6.155 6.887c-1.673.08-3.352.136-5.045.136-59.276 0-107.33-48.053-107.33-107.33 0-57.863 45.79-105.02 103.11-107.24l-5.896 7.424c-45.017 4.51-81.248 38.772-88.761 82.837" fill="#62aeb2"/><path d="M454.56 264.87a148.932 148.932 0 0 0-3.857 4.131 145.173 145.173 0 0 0-14.674 19.422l-10.18-1.379a154.653 154.653 0 0 1 12.825-18.408 155.187 155.187 0 0 1 10.177-11.368 156.325 156.325 0 0 1 7.121-6.742l10.99 2.959a143.075 143.075 0 0 0-6.091 5.254 144.273 144.273 0 0 0-6.311 6.131M420.05 407.76l-9.694.332a153.96 153.96 0 0 1-5.979-42.596c0-20.788 4.126-40.609 11.596-58.699h10.22c-7.983 17.938-12.422 37.799-12.422 58.699 0 14.704 2.197 28.895 6.279 42.264M458.43 365.5c0-17.657 4.593-34.231 12.636-48.619l4.824 2.261c-.085.15-.164.305-.247.456-.309.553-.611 1.11-.909 1.671a100.017 100.017 0 0 0-3.833 8.142 94.53 94.53 0 0 0-1.251 3.173c-.178.479-.342.965-.512 1.449a95.322 95.322 0 0 0-.813 2.401c-.16.498-.323.994-.476 1.496a92.022 92.022 0 0 0-.986 3.474c-.119.448-.225.9-.337 1.351-.232.939-.45 1.884-.654 2.834-.087.408-.181.812-.263 1.222a93.627 93.627 0 0 0-.674 3.776c-.067.418-.126.838-.186 1.258a94.464 94.464 0 0 0-.425 3.343c-.031.282-.069.562-.097.844a95.78 95.78 0 0 0-.366 5.239 94.86 94.86 0 0 0-.1 3.965c0 .088-.007.175-.007.264 0 .173.012.344.013.517.007 1.306.039 2.606.099 3.898.025.564.069 1.123.105 1.685.058.914.124 1.826.208 2.733a96.424 96.424 0 0 0 .513 4.374c.089.638.178 1.276.28 1.91.132.823.284 1.64.438 2.456.116.616.222 1.236.349 1.848.271 1.292.566 2.576.888 3.849.192.756.41 1.501.62 2.251.166.593.328 1.188.505 1.777.243.808.505 1.608.77 2.407.164.498.33.995.503 1.489.297.848.604 1.692.924 2.531.146.38.298.759.449 1.138.369.93.743 1.856 1.14 2.771l.022.048a94.131 94.131 0 0 0 7.034 13.177c.004.009.01.017.015.025a95.434 95.434 0 0 0 4.24 6.115l-5.094 2.048C465.621 408.009 458.43 387.6 458.43 365.5" fill="#62aeb2"/><path d="m581.16 243.57-2.615 9.418-2.535 9.131c45.885 7.797 81.619 45.39 86.59 92.177.391 3.682.597 7.42.597 11.207 0 57.922-46.956 104.88-104.88 104.88-1.647 0-3.284-.043-4.913-.119a105.226 105.226 0 0 1-11.688-1.194 104.98 104.98 0 0 1-4.422-.797 103.31 103.31 0 0 1-6.613-1.578 104.297 104.297 0 0 1-17.449-6.475 104.871 104.871 0 0 1-18.076-10.988 100.108 100.108 0 0 1-8.89 4.998 114.256 114.256 0 0 0 18.06 12.038 113.806 113.806 0 0 0 10.644 5.02c2.225.913 4.484 1.764 6.778 2.54 2.179.735 4.39 1.399 6.626 2.004 1.455.394 2.919.766 4.395 1.103a115.525 115.525 0 0 0 25.548 2.869c63.125 0 114.3-51.173 114.3-114.3 0-4.357-.253-8.653-.727-12.883l9.607-1.732c-6.345-54.048-47.438-97.468-100.34-107.32m-22.841 27.365c-1.275 0-2.54.041-3.8.098-.676.025-1.35.058-2.024.098-.007 0-.015.003-.023.003a94.109 94.109 0 0 0-53.919 21.088c-.391.323-.775.651-1.161.975a100.902 100.902 0 0 0-3.216 2.834 99.216 99.216 0 0 0-1.786 1.705c-.4.389-.807.772-1.2 1.168a90.995 90.995 0 0 0-2.536 2.66c-.474.516-.932 1.047-1.394 1.574-.414.471-.827.942-1.231 1.418a99.82 99.82 0 0 0-2.487 3.084c-.48.621-.96 1.241-1.424 1.869-.029.041-.06.079-.089.118l-4.694-2.575-4.419-2.419c-12.258 17.162-19.475 38.172-19.475 60.87 0 25.55 9.142 48.961 24.325 67.153l4.675-2.22 4.99-2.369a94.878 94.878 0 0 0 28.026 21.725c.266.137.532.277.803.411.77.384 1.547.755 2.33 1.118.419.194.841.382 1.263.571a93.97 93.97 0 0 0 30.794 7.843h.006c1.216.097 2.434.183 3.66.234 1.31.062 2.624.099 3.948.101H558.316c1.348 0 2.684-.044 4.018-.1l.024-.002a94.398 94.398 0 0 0 4.382-.292c.116-.009.234-.015.352-.026a93.293 93.293 0 0 0 4.156-.483c.171-.023.344-.041.514-.065a95.083 95.083 0 0 0 3.956-.657c.213-.041.431-.075.646-.116a93.744 93.744 0 0 0 3.769-.817c.252-.06.505-.113.756-.175a91.676 91.676 0 0 0 3.593-.963c.283-.082.571-.158.853-.243a92.313 92.313 0 0 0 3.426-1.099c.31-.106.623-.207.933-.316 1.1-.387 2.186-.798 3.267-1.225.332-.13.666-.257.997-.392a96.367 96.367 0 0 0 3.11-1.339c.354-.159.708-.315 1.059-.479a92.603 92.603 0 0 0 2.954-1.441c.371-.189.742-.376 1.109-.569a92.422 92.422 0 0 0 2.801-1.535c.387-.219.771-.439 1.154-.664a93.505 93.505 0 0 0 2.648-1.617c.399-.253.796-.506 1.192-.764a92.352 92.352 0 0 0 2.493-1.69 89.785 89.785 0 0 0 4.812-3.6 95.347 95.347 0 0 0 5.505-4.752c.426-.397.846-.802 1.267-1.207a96.934 96.934 0 0 0 1.904-1.89c.426-.437.845-.881 1.263-1.326a96.568 96.568 0 0 0 1.761-1.918c.425-.478.84-.964 1.255-1.449a96.685 96.685 0 0 0 1.621-1.939c.419-.518.826-1.045 1.235-1.572.501-.647 1-1.295 1.485-1.954.41-.559.806-1.128 1.205-1.696.455-.649.91-1.3 1.35-1.962.4-.601.783-1.212 1.168-1.822.412-.652.825-1.301 1.22-1.963.384-.643.752-1.297 1.121-1.95.368-.65.738-1.297 1.092-1.956.367-.687.715-1.386 1.065-2.083.326-.644.655-1.286.965-1.939.348-.731.673-1.474 1.002-2.216.283-.637.573-1.272.842-1.917.324-.778.624-1.567.929-2.354.242-.627.493-1.25.722-1.883.299-.828.571-1.668.848-2.506.201-.61.413-1.215.602-1.83.272-.884.513-1.781.759-2.676.162-.585.335-1.164.486-1.753.242-.952.451-1.916.665-2.879.12-.546.256-1.087.367-1.638.211-1.038.384-2.089.56-3.138.082-.488.18-.97.255-1.461.178-1.174.316-2.361.451-3.549.042-.376.1-.747.139-1.124.148-1.468.256-2.947.337-4.434.005-.103.018-.205.023-.307.082-1.609.125-3.227.125-4.855 0-1.619-.044-3.227-.124-4.826-.013-.244-.04-.484-.054-.728a93.422 93.422 0 0 0-.304-3.987c-.045-.459-.114-.911-.167-1.368a93.13 93.13 0 0 0-.416-3.278c-.083-.549-.189-1.089-.28-1.634-.167-.983-.329-1.966-.526-2.937-.119-.594-.261-1.179-.391-1.769-.203-.911-.402-1.823-.629-2.724-.157-.62-.335-1.231-.505-1.847-.234-.855-.467-1.711-.726-2.557-.195-.639-.41-1.267-.617-1.901-.265-.806-.53-1.613-.816-2.411-.234-.65-.484-1.291-.732-1.933-.294-.764-.586-1.527-.899-2.28-.272-.658-.561-1.307-.849-1.957a90.549 90.549 0 0 0-.971-2.152c-.314-.663-.641-1.317-.968-1.972a94.17 94.17 0 0 0-1.038-2.026 94.763 94.763 0 0 0-1.089-1.98 93.772 93.772 0 0 0-1.095-1.902 93.042 93.042 0 0 0-1.213-1.985c-.375-.595-.753-1.189-1.14-1.779a109.172 109.172 0 0 0-2.519-3.635 91.938 91.938 0 0 0-2.676-3.498 92.36 92.36 0 0 0-1.617-1.969 109.552 109.552 0 0 0-2.974-3.353 94.078 94.078 0 0 0-1.194-1.25 91.336 91.336 0 0 0-1.932-1.955c-.383-.375-.775-.74-1.164-1.111a96.75 96.75 0 0 0-2.105-1.942c-.366-.324-.739-.641-1.11-.961a99.453 99.453 0 0 0-2.303-1.937c-.334-.27-.676-.53-1.014-.797a95.448 95.448 0 0 0-2.53-1.934c-.289-.213-.586-.416-.879-.625a94.486 94.486 0 0 0-2.792-1.941c-.221-.145-.447-.283-.669-.428a95.711 95.711 0 0 0-3.124-1.961c-.113-.068-.229-.131-.342-.197a93.997 93.997 0 0 0-42.053-12.928 2.428 2.428 0 0 0-.168-.014c-.021-.003-.04-.003-.061-.005a91.893 91.893 0 0 0-1.978-.096 84.71 84.71 0 0 0-3.815-.098m-85.397-22.062a120.25 120.25 0 0 0-31.241-4.131c-66.062 0-119.62 53.557-119.62 119.62 0 66.06 53.553 119.61 119.62 119.61a120.528 120.528 0 0 0 28.968-3.539 119.713 119.713 0 0 0 8.984-2.611 137.84 137.84 0 0 1-11.885-9.36 137.608 137.608 0 0 1-6.202-5.799 136.297 136.297 0 0 1-3.749-3.893c-22.715-24.472-36.612-57.243-36.612-93.266 0-36.992 14.653-70.558 38.464-95.226a139.838 139.838 0 0 1 3.856-3.846 137.803 137.803 0 0 1 6.308-5.668 137.887 137.887 0 0 1 12.028-9.103 119.98 119.98 0 0 0-8.919-2.787m221.27 98.007a139.13 139.13 0 0 1 1.263 18.623c0 16.046-2.766 31.444-7.829 45.756a133.266 133.266 0 0 1-1.724 4.626l6.893 2.746 8.729 3.476a153.345 153.345 0 0 1-10.98 22.267l-7.722-5.398-6.098-4.262a137.377 137.377 0 0 1-4.129 6.611c-24.569 36.953-66.575 61.311-114.28 61.311a137.187 137.187 0 0 1-50.301-9.519 137.253 137.253 0 0 1-4.386-1.829c-2.231-.972-4.43-2-6.597-3.084a136.804 136.804 0 0 1-6.785-3.63 128.316 128.316 0 0 1-21.044 6.476 130.064 130.064 0 0 1-27.525 2.939c-5.952 0-11.807-.41-17.545-1.186v11.687c-47.353-5.868-87.438-35.177-108.28-75.958-1.506-2.943.317 1.31-.982-1.749l-12.972 2.059c-4.076-8.46-7.828-18.427-10.767-32.195l24.257-2.727c-.699-2.652-.383-1.799-.915-4.514a130.18 130.18 0 0 1-2.428-25.048c0-71.595 58.038-129.63 129.63-129.63 10.329 0 20.368 1.233 30 3.516 4.071.965 8.063 2.127 11.975 3.465.891.304 1.789.594 2.672.916 2.115.775 4.194 1.625 6.256 2.506a136.833 136.833 0 0 1 17.947-8.194 136.293 136.293 0 0 1 11.726-3.763 137.333 137.333 0 0 1 36.062-4.801c64.248 0 118.17 44.185 133.05 103.83l7.379-1.056c-4.883-20.026-13.934-38.415-26.188-54.198l8.534-4.31c15.737 20.773 26.325 45.657 29.862 72.763l-9.37.83z" fill="#00868e"/><path d="M554.65 454.96a89.134 89.134 0 0 1-28.527-5.896c21.724-21.656 35.173-51.611 35.173-84.709 0-13.873-2.363-27.191-6.707-39.58l-12.005 2.938c4.153 11.435 6.425 23.771 6.425 36.642 0 31.303-13.42 59.455-34.8 79.071a89.816 89.816 0 0 1-9.978-6.562c20.346-17.563 33.238-43.523 33.238-72.509 0-28.309-12.292-53.734-31.815-71.269 14.298-10.416 31.794-16.689 50.743-17.09 18.867 24.449 30.103 55.086 30.103 88.359 0 34.293-11.926 65.797-31.85 90.605" fill="#00868e"/><path d="m393.19 500.78-2.466 8.942c-37.188-13.021-67.852-39.919-85.788-74.484l9.666-1.535c16.934 30.994 44.882 55.11 78.588 67.077" fill="#e5ad23"/></g></svg>
\ No newline at end of file
diff --git a/neosensor/libraries/ArduinoJson/src/ArduinoJson.h b/neosensor/libraries/ArduinoJson/src/ArduinoJson.h
index f79212db..c9ac0ca6 100644
--- a/neosensor/libraries/ArduinoJson/src/ArduinoJson.h
+++ b/neosensor/libraries/ArduinoJson/src/ArduinoJson.h
@@ -1,5 +1,5 @@
 // ArduinoJson - https://arduinojson.org
-// Copyright © 2014-2022, Benoit BLANCHON
+// Copyright © 2014-2023, Benoit BLANCHON
 // MIT License
 
 #pragma once
diff --git a/neosensor/libraries/ArduinoJson/src/ArduinoJson.hpp b/neosensor/libraries/ArduinoJson/src/ArduinoJson.hpp
index 3a490f33..2d1b0be1 100644
--- a/neosensor/libraries/ArduinoJson/src/ArduinoJson.hpp
+++ b/neosensor/libraries/ArduinoJson/src/ArduinoJson.hpp
@@ -1,15 +1,20 @@
 // ArduinoJson - https://arduinojson.org
-// Copyright © 2014-2022, Benoit BLANCHON
+// Copyright © 2014-2023, Benoit BLANCHON
 // MIT License
 
 #pragma once
 
+#if __cplusplus < 201103L && (!defined(_MSC_VER) || _MSC_VER < 1910)
+#  error ArduinoJson requires C++11 or newer. Configure your compiler for C++11 or downgrade ArduinoJson to 6.20.
+#endif
+
 #include "ArduinoJson/Configuration.hpp"
 
 // Include Arduino.h before stdlib.h to avoid conflict with atexit()
 // https://github.com/bblanchon/ArduinoJson/pull/1693#issuecomment-1001060240
 #if ARDUINOJSON_ENABLE_ARDUINO_STRING || ARDUINOJSON_ENABLE_ARDUINO_STREAM || \
-    ARDUINOJSON_ENABLE_ARDUINO_PRINT || ARDUINOJSON_ENABLE_PROGMEM
+    ARDUINOJSON_ENABLE_ARDUINO_PRINT ||                                       \
+    (ARDUINOJSON_ENABLE_PROGMEM && defined(ARDUINO))
 #  include <Arduino.h>
 #endif
 
@@ -45,36 +50,3 @@
 #include "ArduinoJson/MsgPack/MsgPackSerializer.hpp"
 
 #include "ArduinoJson/compatibility.hpp"
-
-namespace ArduinoJson {
-using ARDUINOJSON_NAMESPACE::BasicJsonDocument;
-using ARDUINOJSON_NAMESPACE::copyArray;
-using ARDUINOJSON_NAMESPACE::DeserializationError;
-using ARDUINOJSON_NAMESPACE::deserializeJson;
-using ARDUINOJSON_NAMESPACE::deserializeMsgPack;
-using ARDUINOJSON_NAMESPACE::DynamicJsonDocument;
-using ARDUINOJSON_NAMESPACE::JsonArray;
-using ARDUINOJSON_NAMESPACE::JsonArrayConst;
-using ARDUINOJSON_NAMESPACE::JsonDocument;
-using ARDUINOJSON_NAMESPACE::JsonFloat;
-using ARDUINOJSON_NAMESPACE::JsonInteger;
-using ARDUINOJSON_NAMESPACE::JsonObject;
-using ARDUINOJSON_NAMESPACE::JsonObjectConst;
-using ARDUINOJSON_NAMESPACE::JsonPair;
-using ARDUINOJSON_NAMESPACE::JsonPairConst;
-using ARDUINOJSON_NAMESPACE::JsonString;
-using ARDUINOJSON_NAMESPACE::JsonUInt;
-using ARDUINOJSON_NAMESPACE::JsonVariant;
-using ARDUINOJSON_NAMESPACE::JsonVariantConst;
-using ARDUINOJSON_NAMESPACE::measureJson;
-using ARDUINOJSON_NAMESPACE::serialized;
-using ARDUINOJSON_NAMESPACE::serializeJson;
-using ARDUINOJSON_NAMESPACE::serializeJsonPretty;
-using ARDUINOJSON_NAMESPACE::serializeMsgPack;
-using ARDUINOJSON_NAMESPACE::StaticJsonDocument;
-
-namespace DeserializationOption {
-using ARDUINOJSON_NAMESPACE::Filter;
-using ARDUINOJSON_NAMESPACE::NestingLimit;
-}  // namespace DeserializationOption
-}  // namespace ArduinoJson
diff --git a/neosensor/libraries/ArduinoJson/src/ArduinoJson/Array/ElementProxy.hpp b/neosensor/libraries/ArduinoJson/src/ArduinoJson/Array/ElementProxy.hpp
index b3e00a2c..d6e9aa5d 100644
--- a/neosensor/libraries/ArduinoJson/src/ArduinoJson/Array/ElementProxy.hpp
+++ b/neosensor/libraries/ArduinoJson/src/ArduinoJson/Array/ElementProxy.hpp
@@ -1,26 +1,26 @@
 // ArduinoJson - https://arduinojson.org
-// Copyright © 2014-2022, Benoit BLANCHON
+// Copyright © 2014-2023, Benoit BLANCHON
 // MIT License
 
 #pragma once
 
 #include <ArduinoJson/Variant/VariantRefBase.hpp>
 
-namespace ARDUINOJSON_NAMESPACE {
+ARDUINOJSON_BEGIN_PRIVATE_NAMESPACE
 
 // A proxy class to get or set an element of an array.
 // https://arduinojson.org/v6/api/jsonarray/subscript/
 template <typename TUpstream>
-class ElementProxy : public VariantRefBase<ElementProxy<TUpstream> >,
-                     public VariantOperators<ElementProxy<TUpstream> > {
+class ElementProxy : public VariantRefBase<ElementProxy<TUpstream>>,
+                     public VariantOperators<ElementProxy<TUpstream>> {
   friend class VariantAttorney;
 
  public:
   ElementProxy(TUpstream upstream, size_t index)
-      : _upstream(upstream), _index(index) {}
+      : upstream_(upstream), index_(index) {}
 
   ElementProxy(const ElementProxy& src)
-      : _upstream(src._upstream), _index(src._index) {}
+      : upstream_(src.upstream_), index_(src.index_) {}
 
   FORCE_INLINE ElementProxy& operator=(const ElementProxy& src) {
     this->set(src);
@@ -41,20 +41,20 @@ class ElementProxy : public VariantRefBase<ElementProxy<TUpstream> >,
 
  private:
   FORCE_INLINE MemoryPool* getPool() const {
-    return VariantAttorney::getPool(_upstream);
+    return VariantAttorney::getPool(upstream_);
   }
 
   FORCE_INLINE VariantData* getData() const {
-    return variantGetElement(VariantAttorney::getData(_upstream), _index);
+    return variantGetElement(VariantAttorney::getData(upstream_), index_);
   }
 
   FORCE_INLINE VariantData* getOrCreateData() const {
-    return variantGetOrAddElement(VariantAttorney::getOrCreateData(_upstream),
-                                  _index, VariantAttorney::getPool(_upstream));
+    return variantGetOrAddElement(VariantAttorney::getOrCreateData(upstream_),
+                                  index_, VariantAttorney::getPool(upstream_));
   }
 
-  TUpstream _upstream;
-  size_t _index;
+  TUpstream upstream_;
+  size_t index_;
 };
 
-}  // namespace ARDUINOJSON_NAMESPACE
+ARDUINOJSON_END_PRIVATE_NAMESPACE
diff --git a/neosensor/libraries/ArduinoJson/src/ArduinoJson/Array/JsonArray.hpp b/neosensor/libraries/ArduinoJson/src/ArduinoJson/Array/JsonArray.hpp
index a26b1f1f..d208e417 100644
--- a/neosensor/libraries/ArduinoJson/src/ArduinoJson/Array/JsonArray.hpp
+++ b/neosensor/libraries/ArduinoJson/src/ArduinoJson/Array/JsonArray.hpp
@@ -1,5 +1,5 @@
 // ArduinoJson - https://arduinojson.org
-// Copyright © 2014-2022, Benoit BLANCHON
+// Copyright © 2014-2023, Benoit BLANCHON
 // MIT License
 
 #pragma once
@@ -7,45 +7,45 @@
 #include <ArduinoJson/Array/ElementProxy.hpp>
 #include <ArduinoJson/Array/JsonArrayConst.hpp>
 
-namespace ARDUINOJSON_NAMESPACE {
+ARDUINOJSON_BEGIN_PUBLIC_NAMESPACE
 
 class JsonObject;
 
 // A reference to an array in a JsonDocument
 // https://arduinojson.org/v6/api/jsonarray/
-class JsonArray : public VariantOperators<JsonArray> {
-  friend class VariantAttorney;
+class JsonArray : public detail::VariantOperators<JsonArray> {
+  friend class detail::VariantAttorney;
 
  public:
   typedef JsonArrayIterator iterator;
 
   // Constructs an unbound reference.
-  FORCE_INLINE JsonArray() : _data(0), _pool(0) {}
+  FORCE_INLINE JsonArray() : data_(0), pool_(0) {}
 
   // INTERNAL USE ONLY
-  FORCE_INLINE JsonArray(MemoryPool* pool, CollectionData* data)
-      : _data(data), _pool(pool) {}
+  FORCE_INLINE JsonArray(detail::MemoryPool* pool, detail::CollectionData* data)
+      : data_(data), pool_(pool) {}
 
   // Returns a JsonVariant pointing to the array.
   // https://arduinojson.org/v6/api/jsonvariant/
   operator JsonVariant() {
-    void* data = _data;  // prevent warning cast-align
-    return JsonVariant(_pool, reinterpret_cast<VariantData*>(data));
+    void* data = data_;  // prevent warning cast-align
+    return JsonVariant(pool_, reinterpret_cast<detail::VariantData*>(data));
   }
 
   // Returns a read-only reference to the array.
   // https://arduinojson.org/v6/api/jsonarrayconst/
   operator JsonArrayConst() const {
-    return JsonArrayConst(_data);
+    return JsonArrayConst(data_);
   }
 
   // Appends a new (null) element to the array.
   // Returns a reference to the new element.
   // https://arduinojson.org/v6/api/jsonarray/add/
   JsonVariant add() const {
-    if (!_data)
+    if (!data_)
       return JsonVariant();
-    return JsonVariant(_pool, _data->addElement(_pool));
+    return JsonVariant(pool_, data_->addElement(pool_));
   }
 
   // Appends a value to the array.
@@ -65,9 +65,9 @@ class JsonArray : public VariantOperators<JsonArray> {
   // Returns an iterator to the first element of the array.
   // https://arduinojson.org/v6/api/jsonarray/begin/
   FORCE_INLINE iterator begin() const {
-    if (!_data)
+    if (!data_)
       return iterator();
-    return iterator(_pool, _data->head());
+    return iterator(pool_, data_->head());
   }
 
   // Returns an iterator following the last element of the array.
@@ -79,47 +79,47 @@ class JsonArray : public VariantOperators<JsonArray> {
   // Copies an array.
   // https://arduinojson.org/v6/api/jsonarray/set/
   FORCE_INLINE bool set(JsonArrayConst src) const {
-    if (!_data || !src._data)
+    if (!data_ || !src.data_)
       return false;
-    return _data->copyFrom(*src._data, _pool);
+    return data_->copyFrom(*src.data_, pool_);
   }
 
   // Compares the content of two arrays.
   FORCE_INLINE bool operator==(JsonArray rhs) const {
-    return JsonArrayConst(_data) == JsonArrayConst(rhs._data);
+    return JsonArrayConst(data_) == JsonArrayConst(rhs.data_);
   }
 
   // Removes the element at the specified iterator.
   // ⚠️ Doesn't release the memory associated with the removed element.
   // https://arduinojson.org/v6/api/jsonarray/remove/
   FORCE_INLINE void remove(iterator it) const {
-    if (!_data)
+    if (!data_)
       return;
-    _data->removeSlot(it._slot);
+    data_->removeSlot(it.slot_);
   }
 
   // Removes the element at the specified index.
   // ⚠️ Doesn't release the memory associated with the removed element.
   // https://arduinojson.org/v6/api/jsonarray/remove/
   FORCE_INLINE void remove(size_t index) const {
-    if (!_data)
+    if (!data_)
       return;
-    _data->removeElement(index);
+    data_->removeElement(index);
   }
 
   // Removes all the elements of the array.
   // ⚠️ Doesn't release the memory associated with the removed elements.
   // https://arduinojson.org/v6/api/jsonarray/clear/
   void clear() const {
-    if (!_data)
+    if (!data_)
       return;
-    _data->clear();
+    data_->clear();
   }
 
   // Gets or sets the element at the specified index.
   // https://arduinojson.org/v6/api/jsonarray/subscript/
-  FORCE_INLINE ElementProxy<JsonArray> operator[](size_t index) const {
-    return ElementProxy<JsonArray>(*this, index);
+  FORCE_INLINE detail::ElementProxy<JsonArray> operator[](size_t index) const {
+    return {*this, index};
   }
 
   // Creates an object and appends it to the array.
@@ -133,69 +133,69 @@ class JsonArray : public VariantOperators<JsonArray> {
   }
 
   operator JsonVariantConst() const {
-    return JsonVariantConst(collectionToVariant(_data));
+    return JsonVariantConst(collectionToVariant(data_));
   }
 
   // Returns true if the reference is unbound.
   // https://arduinojson.org/v6/api/jsonarray/isnull/
   FORCE_INLINE bool isNull() const {
-    return _data == 0;
+    return data_ == 0;
   }
 
   // Returns true if the reference is bound.
   // https://arduinojson.org/v6/api/jsonarray/isnull/
   FORCE_INLINE operator bool() const {
-    return _data != 0;
+    return data_ != 0;
   }
 
   // Returns the number of bytes occupied by the array.
   // https://arduinojson.org/v6/api/jsonarray/memoryusage/
   FORCE_INLINE size_t memoryUsage() const {
-    return _data ? _data->memoryUsage() : 0;
+    return data_ ? data_->memoryUsage() : 0;
   }
 
   // Returns the depth (nesting level) of the array.
   // https://arduinojson.org/v6/api/jsonarray/nesting/
   FORCE_INLINE size_t nesting() const {
-    return variantNesting(collectionToVariant(_data));
+    return variantNesting(collectionToVariant(data_));
   }
 
   // Returns the number of elements in the array.
   // https://arduinojson.org/v6/api/jsonarray/size/
   FORCE_INLINE size_t size() const {
-    return _data ? _data->size() : 0;
+    return data_ ? data_->size() : 0;
   }
 
  private:
-  MemoryPool* getPool() const {
-    return _pool;
+  detail::MemoryPool* getPool() const {
+    return pool_;
   }
 
-  VariantData* getData() const {
-    return collectionToVariant(_data);
+  detail::VariantData* getData() const {
+    return collectionToVariant(data_);
   }
 
-  VariantData* getOrCreateData() const {
-    return collectionToVariant(_data);
+  detail::VariantData* getOrCreateData() const {
+    return collectionToVariant(data_);
   }
 
-  CollectionData* _data;
-  MemoryPool* _pool;
+  detail::CollectionData* data_;
+  detail::MemoryPool* pool_;
 };
 
 template <>
-struct Converter<JsonArray> : private VariantAttorney {
+struct Converter<JsonArray> : private detail::VariantAttorney {
   static void toJson(JsonVariantConst src, JsonVariant dst) {
     variantCopyFrom(getData(dst), getData(src), getPool(dst));
   }
 
   static JsonArray fromJson(JsonVariant src) {
-    VariantData* data = getData(src);
-    MemoryPool* pool = getPool(src);
+    auto data = getData(src);
+    auto pool = getPool(src);
     return JsonArray(pool, data != 0 ? data->asArray() : 0);
   }
 
-  static InvalidConversion<JsonVariantConst, JsonArray> fromJson(
+  static detail::InvalidConversion<JsonVariantConst, JsonArray> fromJson(
       JsonVariantConst);
 
   static bool checkJson(JsonVariantConst) {
@@ -203,8 +203,9 @@ struct Converter<JsonArray> : private VariantAttorney {
   }
 
   static bool checkJson(JsonVariant src) {
-    VariantData* data = getData(src);
+    auto data = getData(src);
     return data && data->isArray();
   }
 };
-}  // namespace ARDUINOJSON_NAMESPACE
+
+ARDUINOJSON_END_PUBLIC_NAMESPACE
diff --git a/neosensor/libraries/ArduinoJson/src/ArduinoJson/Array/JsonArrayConst.hpp b/neosensor/libraries/ArduinoJson/src/ArduinoJson/Array/JsonArrayConst.hpp
index 17e65a4b..6a6463c8 100644
--- a/neosensor/libraries/ArduinoJson/src/ArduinoJson/Array/JsonArrayConst.hpp
+++ b/neosensor/libraries/ArduinoJson/src/ArduinoJson/Array/JsonArrayConst.hpp
@@ -1,5 +1,5 @@
 // ArduinoJson - https://arduinojson.org
-// Copyright © 2014-2022, Benoit BLANCHON
+// Copyright © 2014-2023, Benoit BLANCHON
 // MIT License
 
 #pragma once
@@ -8,15 +8,15 @@
 #include <ArduinoJson/Variant/VariantAttorney.hpp>
 #include <ArduinoJson/Variant/VariantData.hpp>
 
-namespace ARDUINOJSON_NAMESPACE {
+ARDUINOJSON_BEGIN_PUBLIC_NAMESPACE
 
 class JsonObject;
 
 // A read-only reference to an array in a JsonDocument
 // https://arduinojson.org/v6/api/jsonarrayconst/
-class JsonArrayConst : public VariantOperators<JsonArrayConst> {
+class JsonArrayConst : public detail::VariantOperators<JsonArrayConst> {
   friend class JsonArray;
-  friend class VariantAttorney;
+  friend class detail::VariantAttorney;
 
  public:
   typedef JsonArrayConstIterator iterator;
@@ -24,9 +24,9 @@ class JsonArrayConst : public VariantOperators<JsonArrayConst> {
   // Returns an iterator to the first element of the array.
   // https://arduinojson.org/v6/api/jsonarrayconst/begin/
   FORCE_INLINE iterator begin() const {
-    if (!_data)
+    if (!data_)
       return iterator();
-    return iterator(_data->head());
+    return iterator(data_->head());
   }
 
   // Returns an iterator to the element following the last element of the array.
@@ -36,17 +36,18 @@ class JsonArrayConst : public VariantOperators<JsonArrayConst> {
   }
 
   // Creates an unbound reference.
-  FORCE_INLINE JsonArrayConst() : _data(0) {}
+  FORCE_INLINE JsonArrayConst() : data_(0) {}
 
   // INTERNAL USE ONLY
-  FORCE_INLINE JsonArrayConst(const CollectionData* data) : _data(data) {}
+  FORCE_INLINE JsonArrayConst(const detail::CollectionData* data)
+      : data_(data) {}
 
   // Compares the content of two arrays.
   // Returns true if the two arrays are equal.
   FORCE_INLINE bool operator==(JsonArrayConst rhs) const {
-    if (_data == rhs._data)
+    if (data_ == rhs.data_)
       return true;
-    if (!_data || !rhs._data)
+    if (!data_ || !rhs.data_)
       return false;
 
     iterator it1 = begin();
@@ -69,66 +70,66 @@ class JsonArrayConst : public VariantOperators<JsonArrayConst> {
   // Returns the element at the specified index.
   // https://arduinojson.org/v6/api/jsonarrayconst/subscript/
   FORCE_INLINE JsonVariantConst operator[](size_t index) const {
-    return JsonVariantConst(_data ? _data->getElement(index) : 0);
+    return JsonVariantConst(data_ ? data_->getElement(index) : 0);
   }
 
   operator JsonVariantConst() const {
-    return JsonVariantConst(collectionToVariant(_data));
+    return JsonVariantConst(collectionToVariant(data_));
   }
 
   // Returns true if the reference is unbound.
   // https://arduinojson.org/v6/api/jsonarrayconst/isnull/
   FORCE_INLINE bool isNull() const {
-    return _data == 0;
+    return data_ == 0;
   }
 
   // Returns true if the reference is bound.
   // https://arduinojson.org/v6/api/jsonarrayconst/isnull/
   FORCE_INLINE operator bool() const {
-    return _data != 0;
+    return data_ != 0;
   }
 
   // Returns the number of bytes occupied by the array.
   // https://arduinojson.org/v6/api/jsonarrayconst/memoryusage/
   FORCE_INLINE size_t memoryUsage() const {
-    return _data ? _data->memoryUsage() : 0;
+    return data_ ? data_->memoryUsage() : 0;
   }
 
   // Returns the depth (nesting level) of the array.
   // https://arduinojson.org/v6/api/jsonarrayconst/nesting/
   FORCE_INLINE size_t nesting() const {
-    return variantNesting(collectionToVariant(_data));
+    return variantNesting(collectionToVariant(data_));
   }
 
   // Returns the number of elements in the array.
   // https://arduinojson.org/v6/api/jsonarrayconst/size/
   FORCE_INLINE size_t size() const {
-    return _data ? _data->size() : 0;
+    return data_ ? data_->size() : 0;
   }
 
  private:
-  const VariantData* getData() const {
-    return collectionToVariant(_data);
+  const detail::VariantData* getData() const {
+    return collectionToVariant(data_);
   }
 
-  const CollectionData* _data;
+  const detail::CollectionData* data_;
 };
 
 template <>
-struct Converter<JsonArrayConst> : private VariantAttorney {
+struct Converter<JsonArrayConst> : private detail::VariantAttorney {
   static void toJson(JsonVariantConst src, JsonVariant dst) {
     variantCopyFrom(getData(dst), getData(src), getPool(dst));
   }
 
   static JsonArrayConst fromJson(JsonVariantConst src) {
-    const VariantData* data = getData(src);
+    auto data = getData(src);
     return data ? data->asArray() : 0;
   }
 
   static bool checkJson(JsonVariantConst src) {
-    const VariantData* data = getData(src);
+    auto data = getData(src);
     return data && data->isArray();
   }
 };
 
-}  // namespace ARDUINOJSON_NAMESPACE
+ARDUINOJSON_END_PUBLIC_NAMESPACE
diff --git a/neosensor/libraries/ArduinoJson/src/ArduinoJson/Array/JsonArrayImpl.hpp b/neosensor/libraries/ArduinoJson/src/ArduinoJson/Array/JsonArrayImpl.hpp
index 45a520f4..dc0487af 100644
--- a/neosensor/libraries/ArduinoJson/src/ArduinoJson/Array/JsonArrayImpl.hpp
+++ b/neosensor/libraries/ArduinoJson/src/ArduinoJson/Array/JsonArrayImpl.hpp
@@ -1,5 +1,5 @@
 // ArduinoJson - https://arduinojson.org
-// Copyright © 2014-2022, Benoit BLANCHON
+// Copyright © 2014-2023, Benoit BLANCHON
 // MIT License
 
 #pragma once
@@ -7,12 +7,16 @@
 #include <ArduinoJson/Array/JsonArray.hpp>
 #include <ArduinoJson/Object/JsonObject.hpp>
 
-namespace ARDUINOJSON_NAMESPACE {
+ARDUINOJSON_BEGIN_PUBLIC_NAMESPACE
 
 inline JsonObject JsonArray::createNestedObject() const {
   return add().to<JsonObject>();
 }
 
+ARDUINOJSON_END_PUBLIC_NAMESPACE
+
+ARDUINOJSON_BEGIN_PRIVATE_NAMESPACE
+
 template <typename TDerived>
 inline JsonArray VariantRefBase<TDerived>::createNestedArray() const {
   return add().template to<JsonArray>();
@@ -29,4 +33,4 @@ inline ElementProxy<TDerived> VariantRefBase<TDerived>::operator[](
   return ElementProxy<TDerived>(derived(), index);
 }
 
-}  // namespace ARDUINOJSON_NAMESPACE
+ARDUINOJSON_END_PRIVATE_NAMESPACE
diff --git a/neosensor/libraries/ArduinoJson/src/ArduinoJson/Array/JsonArrayIterator.hpp b/neosensor/libraries/ArduinoJson/src/ArduinoJson/Array/JsonArrayIterator.hpp
index 382d1177..d9048b28 100644
--- a/neosensor/libraries/ArduinoJson/src/ArduinoJson/Array/JsonArrayIterator.hpp
+++ b/neosensor/libraries/ArduinoJson/src/ArduinoJson/Array/JsonArrayIterator.hpp
@@ -1,5 +1,5 @@
 // ArduinoJson - https://arduinojson.org
-// Copyright © 2014-2022, Benoit BLANCHON
+// Copyright © 2014-2023, Benoit BLANCHON
 // MIT License
 
 #pragma once
@@ -7,111 +7,115 @@
 #include <ArduinoJson/Variant/JsonVariant.hpp>
 #include <ArduinoJson/Variant/SlotFunctions.hpp>
 
-namespace ARDUINOJSON_NAMESPACE {
+ARDUINOJSON_BEGIN_PUBLIC_NAMESPACE
 
 class VariantPtr {
  public:
-  VariantPtr(MemoryPool* pool, VariantData* data) : _variant(pool, data) {}
+  VariantPtr(detail::MemoryPool* pool, detail::VariantData* data)
+      : variant_(pool, data) {}
 
   JsonVariant* operator->() {
-    return &_variant;
+    return &variant_;
   }
 
   JsonVariant& operator*() {
-    return _variant;
+    return variant_;
   }
 
  private:
-  JsonVariant _variant;
+  JsonVariant variant_;
 };
 
 class JsonArrayIterator {
   friend class JsonArray;
 
  public:
-  JsonArrayIterator() : _slot(0) {}
-  explicit JsonArrayIterator(MemoryPool* pool, VariantSlot* slot)
-      : _pool(pool), _slot(slot) {}
+  JsonArrayIterator() : slot_(0) {}
+  explicit JsonArrayIterator(detail::MemoryPool* pool,
+                             detail::VariantSlot* slot)
+      : pool_(pool), slot_(slot) {}
 
   JsonVariant operator*() const {
-    return JsonVariant(_pool, _slot->data());
+    return JsonVariant(pool_, slot_->data());
   }
   VariantPtr operator->() {
-    return VariantPtr(_pool, _slot->data());
+    return VariantPtr(pool_, slot_->data());
   }
 
   bool operator==(const JsonArrayIterator& other) const {
-    return _slot == other._slot;
+    return slot_ == other.slot_;
   }
 
   bool operator!=(const JsonArrayIterator& other) const {
-    return _slot != other._slot;
+    return slot_ != other.slot_;
   }
 
   JsonArrayIterator& operator++() {
-    _slot = _slot->next();
+    slot_ = slot_->next();
     return *this;
   }
 
   JsonArrayIterator& operator+=(size_t distance) {
-    _slot = _slot->next(distance);
+    slot_ = slot_->next(distance);
     return *this;
   }
 
  private:
-  MemoryPool* _pool;
-  VariantSlot* _slot;
+  detail::MemoryPool* pool_;
+  detail::VariantSlot* slot_;
 };
 
 class VariantConstPtr {
  public:
-  VariantConstPtr(const VariantData* data) : _variant(data) {}
+  VariantConstPtr(const detail::VariantData* data) : variant_(data) {}
 
   JsonVariantConst* operator->() {
-    return &_variant;
+    return &variant_;
   }
 
   JsonVariantConst& operator*() {
-    return _variant;
+    return variant_;
   }
 
  private:
-  JsonVariantConst _variant;
+  JsonVariantConst variant_;
 };
 
 class JsonArrayConstIterator {
   friend class JsonArray;
 
  public:
-  JsonArrayConstIterator() : _slot(0) {}
-  explicit JsonArrayConstIterator(const VariantSlot* slot) : _slot(slot) {}
+  JsonArrayConstIterator() : slot_(0) {}
+  explicit JsonArrayConstIterator(const detail::VariantSlot* slot)
+      : slot_(slot) {}
 
   JsonVariantConst operator*() const {
-    return JsonVariantConst(_slot->data());
+    return JsonVariantConst(slot_->data());
   }
   VariantConstPtr operator->() {
-    return VariantConstPtr(_slot->data());
+    return VariantConstPtr(slot_->data());
   }
 
   bool operator==(const JsonArrayConstIterator& other) const {
-    return _slot == other._slot;
+    return slot_ == other.slot_;
   }
 
   bool operator!=(const JsonArrayConstIterator& other) const {
-    return _slot != other._slot;
+    return slot_ != other.slot_;
   }
 
   JsonArrayConstIterator& operator++() {
-    _slot = _slot->next();
+    slot_ = slot_->next();
     return *this;
   }
 
   JsonArrayConstIterator& operator+=(size_t distance) {
-    _slot = _slot->next(distance);
+    slot_ = slot_->next(distance);
     return *this;
   }
 
  private:
-  const VariantSlot* _slot;
+  const detail::VariantSlot* slot_;
 };
-}  // namespace ARDUINOJSON_NAMESPACE
+
+ARDUINOJSON_END_PUBLIC_NAMESPACE
diff --git a/neosensor/libraries/ArduinoJson/src/ArduinoJson/Array/Utilities.hpp b/neosensor/libraries/ArduinoJson/src/ArduinoJson/Array/Utilities.hpp
index 1bff6830..398e11d4 100644
--- a/neosensor/libraries/ArduinoJson/src/ArduinoJson/Array/Utilities.hpp
+++ b/neosensor/libraries/ArduinoJson/src/ArduinoJson/Array/Utilities.hpp
@@ -1,5 +1,5 @@
 // ArduinoJson - https://arduinojson.org
-// Copyright © 2014-2022, Benoit BLANCHON
+// Copyright © 2014-2023, Benoit BLANCHON
 // MIT License
 
 #pragma once
@@ -7,21 +7,21 @@
 #include <ArduinoJson/Array/JsonArray.hpp>
 #include <ArduinoJson/Document/JsonDocument.hpp>
 
-namespace ARDUINOJSON_NAMESPACE {
+ARDUINOJSON_BEGIN_PUBLIC_NAMESPACE
 
 // Copies a value to a JsonVariant.
 // This is a degenerated form of copyArray() to stop the recursion.
 template <typename T>
-inline typename enable_if<!is_array<T>::value, bool>::type copyArray(
-    const T& src, JsonVariant dst) {
+inline typename detail::enable_if<!detail::is_array<T>::value, bool>::type
+copyArray(const T& src, JsonVariant dst) {
   return dst.set(src);
 }
 
 // Copies values from an array to a JsonArray or a JsonVariant.
 // https://arduinojson.org/v6/api/misc/copyarray/
 template <typename T, size_t N, typename TDestination>
-inline typename enable_if<!is_base_of<JsonDocument, TDestination>::value,
-                          bool>::type
+inline typename detail::enable_if<
+    !detail::is_base_of<JsonDocument, TDestination>::value, bool>::type
 copyArray(T (&src)[N], const TDestination& dst) {
   return copyArray(src, N, dst);
 }
@@ -29,8 +29,8 @@ copyArray(T (&src)[N], const TDestination& dst) {
 // Copies values from an array to a JsonArray or a JsonVariant.
 // https://arduinojson.org/v6/api/misc/copyarray/
 template <typename T, typename TDestination>
-inline typename enable_if<!is_base_of<JsonDocument, TDestination>::value,
-                          bool>::type
+inline typename detail::enable_if<
+    !detail::is_base_of<JsonDocument, TDestination>::value, bool>::type
 copyArray(const T* src, size_t len, const TDestination& dst) {
   bool ok = true;
   for (size_t i = 0; i < len; i++) {
@@ -63,8 +63,8 @@ inline bool copyArray(const T* src, size_t len, JsonDocument& dst) {
 // Copies a value from a JsonVariant.
 // This is a degenerated form of copyArray() to stop the recursion.
 template <typename T>
-inline typename enable_if<!is_array<T>::value, size_t>::type copyArray(
-    JsonVariantConst src, T& dst) {
+inline typename detail::enable_if<!detail::is_array<T>::value, size_t>::type
+copyArray(JsonVariantConst src, T& dst) {
   dst = src.as<T>();
   return 1;
 }
@@ -103,11 +103,12 @@ inline size_t copyArray(JsonVariantConst src, char (&dst)[N]) {
 // Copies values from a JsonDocument to an array.
 // https://arduinojson.org/v6/api/misc/copyarray/
 template <typename TSource, typename T>
-inline typename enable_if<is_array<T>::value &&
-                              is_base_of<JsonDocument, TSource>::value,
-                          size_t>::type
+inline typename detail::enable_if<
+    detail::is_array<T>::value &&
+        detail::is_base_of<JsonDocument, TSource>::value,
+    size_t>::type
 copyArray(const TSource& src, T& dst) {
   return copyArray(src.template as<JsonArrayConst>(), dst);
 }
 
-}  // namespace ARDUINOJSON_NAMESPACE
+ARDUINOJSON_END_PUBLIC_NAMESPACE
diff --git a/neosensor/libraries/ArduinoJson/src/ArduinoJson/Collection/CollectionData.hpp b/neosensor/libraries/ArduinoJson/src/ArduinoJson/Collection/CollectionData.hpp
index 560e7e33..090c98b9 100644
--- a/neosensor/libraries/ArduinoJson/src/ArduinoJson/Collection/CollectionData.hpp
+++ b/neosensor/libraries/ArduinoJson/src/ArduinoJson/Collection/CollectionData.hpp
@@ -1,5 +1,5 @@
 // ArduinoJson - https://arduinojson.org
-// Copyright © 2014-2022, Benoit BLANCHON
+// Copyright © 2014-2023, Benoit BLANCHON
 // MIT License
 
 #pragma once
@@ -9,15 +9,15 @@
 
 #include <stddef.h>  // size_t
 
-namespace ARDUINOJSON_NAMESPACE {
+ARDUINOJSON_BEGIN_PRIVATE_NAMESPACE
 
 class MemoryPool;
 class VariantData;
 class VariantSlot;
 
 class CollectionData {
-  VariantSlot* _head;
-  VariantSlot* _tail;
+  VariantSlot* head_;
+  VariantSlot* tail_;
 
  public:
   // Must be a POD!
@@ -67,7 +67,7 @@ class CollectionData {
   bool copyFrom(const CollectionData& src, MemoryPool* pool);
 
   VariantSlot* head() const {
-    return _head;
+    return head_;
   }
 
   void movePointers(ptrdiff_t stringDistance, ptrdiff_t variantDistance);
@@ -91,4 +91,5 @@ inline VariantData* collectionToVariant(CollectionData* collection) {
   void* data = collection;  // prevent warning cast-align
   return reinterpret_cast<VariantData*>(data);
 }
-}  // namespace ARDUINOJSON_NAMESPACE
+
+ARDUINOJSON_END_PRIVATE_NAMESPACE
diff --git a/neosensor/libraries/ArduinoJson/src/ArduinoJson/Collection/CollectionImpl.hpp b/neosensor/libraries/ArduinoJson/src/ArduinoJson/Collection/CollectionImpl.hpp
index f933f527..134d5bd6 100644
--- a/neosensor/libraries/ArduinoJson/src/ArduinoJson/Collection/CollectionImpl.hpp
+++ b/neosensor/libraries/ArduinoJson/src/ArduinoJson/Collection/CollectionImpl.hpp
@@ -1,5 +1,5 @@
 // ArduinoJson - https://arduinojson.org
-// Copyright © 2014-2022, Benoit BLANCHON
+// Copyright © 2014-2023, Benoit BLANCHON
 // MIT License
 
 #pragma once
@@ -9,20 +9,20 @@
 #include <ArduinoJson/Strings/StringAdapters.hpp>
 #include <ArduinoJson/Variant/VariantData.hpp>
 
-namespace ARDUINOJSON_NAMESPACE {
+ARDUINOJSON_BEGIN_PRIVATE_NAMESPACE
 
 inline VariantSlot* CollectionData::addSlot(MemoryPool* pool) {
   VariantSlot* slot = pool->allocVariant();
   if (!slot)
     return 0;
 
-  if (_tail) {
-    ARDUINOJSON_ASSERT(pool->owns(_tail));  // Can't alter a linked array/object
-    _tail->setNextNotNull(slot);
-    _tail = slot;
+  if (tail_) {
+    ARDUINOJSON_ASSERT(pool->owns(tail_));  // Can't alter a linked array/object
+    tail_->setNextNotNull(slot);
+    tail_ = slot;
   } else {
-    _head = slot;
-    _tail = slot;
+    head_ = slot;
+    tail_ = slot;
   }
 
   slot->clear();
@@ -45,8 +45,8 @@ inline VariantData* CollectionData::addMember(TAdaptedString key,
 }
 
 inline void CollectionData::clear() {
-  _head = 0;
-  _tail = 0;
+  head_ = 0;
+  tail_ = 0;
 }
 
 template <typename TAdaptedString>
@@ -57,7 +57,7 @@ inline bool CollectionData::containsKey(const TAdaptedString& key) const {
 inline bool CollectionData::copyFrom(const CollectionData& src,
                                      MemoryPool* pool) {
   clear();
-  for (VariantSlot* s = src._head; s; s = s->next()) {
+  for (VariantSlot* s = src.head_; s; s = s->next()) {
     VariantData* var;
     if (s->key() != 0) {
       JsonString key(s->key(),
@@ -78,7 +78,7 @@ template <typename TAdaptedString>
 inline VariantSlot* CollectionData::getSlot(TAdaptedString key) const {
   if (key.isNull())
     return 0;
-  VariantSlot* slot = _head;
+  VariantSlot* slot = head_;
   while (slot) {
     if (stringEquals(key, adaptString(slot->key())))
       break;
@@ -88,13 +88,13 @@ inline VariantSlot* CollectionData::getSlot(TAdaptedString key) const {
 }
 
 inline VariantSlot* CollectionData::getSlot(size_t index) const {
-  if (!_head)
+  if (!head_)
     return 0;
-  return _head->next(index);
+  return head_->next(index);
 }
 
 inline VariantSlot* CollectionData::getPreviousSlot(VariantSlot* target) const {
-  VariantSlot* current = _head;
+  VariantSlot* current = head_;
   while (current) {
     VariantSlot* next = current->next();
     if (next == target)
@@ -132,7 +132,7 @@ inline VariantData* CollectionData::getElement(size_t index) const {
 
 inline VariantData* CollectionData::getOrAddElement(size_t index,
                                                     MemoryPool* pool) {
-  VariantSlot* slot = _head;
+  VariantSlot* slot = head_;
   while (slot && index > 0) {
     slot = slot->next();
     index--;
@@ -154,9 +154,9 @@ inline void CollectionData::removeSlot(VariantSlot* slot) {
   if (prev)
     prev->setNext(next);
   else
-    _head = next;
+    head_ = next;
   if (!next)
-    _tail = prev;
+    tail_ = prev;
 }
 
 inline void CollectionData::removeElement(size_t index) {
@@ -165,7 +165,7 @@ inline void CollectionData::removeElement(size_t index) {
 
 inline size_t CollectionData::memoryUsage() const {
   size_t total = 0;
-  for (VariantSlot* s = _head; s; s = s->next()) {
+  for (VariantSlot* s = head_; s; s = s->next()) {
     total += sizeof(VariantSlot) + s->data()->memoryUsage();
     if (s->ownsKey())
       total += strlen(s->key()) + 1;
@@ -174,7 +174,7 @@ inline size_t CollectionData::memoryUsage() const {
 }
 
 inline size_t CollectionData::size() const {
-  return slotSize(_head);
+  return slotSize(head_);
 }
 
 template <typename T>
@@ -188,10 +188,10 @@ inline void movePointer(T*& p, ptrdiff_t offset) {
 
 inline void CollectionData::movePointers(ptrdiff_t stringDistance,
                                          ptrdiff_t variantDistance) {
-  movePointer(_head, variantDistance);
-  movePointer(_tail, variantDistance);
-  for (VariantSlot* slot = _head; slot; slot = slot->next())
+  movePointer(head_, variantDistance);
+  movePointer(tail_, variantDistance);
+  for (VariantSlot* slot = head_; slot; slot = slot->next())
     slot->movePointers(stringDistance, variantDistance);
 }
 
-}  // namespace ARDUINOJSON_NAMESPACE
+ARDUINOJSON_END_PRIVATE_NAMESPACE
diff --git a/neosensor/libraries/ArduinoJson/src/ArduinoJson/Configuration.hpp b/neosensor/libraries/ArduinoJson/src/ArduinoJson/Configuration.hpp
index ed5687a3..ac0ea660 100644
--- a/neosensor/libraries/ArduinoJson/src/ArduinoJson/Configuration.hpp
+++ b/neosensor/libraries/ArduinoJson/src/ArduinoJson/Configuration.hpp
@@ -1,31 +1,9 @@
 // ArduinoJson - https://arduinojson.org
-// Copyright © 2014-2022, Benoit BLANCHON
+// Copyright © 2014-2023, Benoit BLANCHON
 // MIT License
 
 #pragma once
 
-#if __cplusplus >= 201103L
-#  define ARDUINOJSON_HAS_LONG_LONG 1
-#  define ARDUINOJSON_HAS_RVALUE_REFERENCES 1
-#else
-#  define ARDUINOJSON_HAS_LONG_LONG 0
-#  define ARDUINOJSON_HAS_RVALUE_REFERENCES 0
-#endif
-
-#ifndef ARDUINOJSON_HAS_NULLPTR
-#  if __cplusplus >= 201103L
-#    define ARDUINOJSON_HAS_NULLPTR 1
-#  else
-#    define ARDUINOJSON_HAS_NULLPTR 0
-#  endif
-#endif
-
-#if defined(_MSC_VER) && !ARDUINOJSON_HAS_LONG_LONG
-#  define ARDUINOJSON_HAS_INT64 1
-#else
-#  define ARDUINOJSON_HAS_INT64 0
-#endif
-
 // Support std::istream and std::ostream
 #ifndef ARDUINOJSON_ENABLE_STD_STREAM
 #  ifdef __has_include
@@ -83,8 +61,7 @@
 
 // Store integral values with long (0) or long long (1)
 #ifndef ARDUINOJSON_USE_LONG_LONG
-#  if ARDUINOJSON_HAS_LONG_LONG && defined(__SIZEOF_POINTER__) && \
-          __SIZEOF_POINTER__ >= 4 ||                              \
+#  if defined(__SIZEOF_POINTER__) && __SIZEOF_POINTER__ >= 4 || \
       defined(_MSC_VER)
 #    define ARDUINOJSON_USE_LONG_LONG 1
 #  endif
@@ -153,9 +130,13 @@
 #    define ARDUINOJSON_ENABLE_ARDUINO_PRINT 0
 #  endif
 
-// Disable support for PROGMEM
+// Enable PROGMEM support on AVR only
 #  ifndef ARDUINOJSON_ENABLE_PROGMEM
-#    define ARDUINOJSON_ENABLE_PROGMEM 0
+#    ifdef __AVR__
+#      define ARDUINOJSON_ENABLE_PROGMEM 1
+#    else
+#      define ARDUINOJSON_ENABLE_PROGMEM 0
+#    endif
 #  endif
 
 #endif  // ARDUINO
@@ -230,7 +211,7 @@
 #  endif
 #endif
 
-#if ARDUINOJSON_HAS_NULLPTR && defined(nullptr)
+#if defined(nullptr)
 #  error nullptr is defined as a macro. Remove the faulty #define or #undef nullptr
 // See https://github.com/bblanchon/ArduinoJson/issues/1355
 #endif
diff --git a/neosensor/libraries/ArduinoJson/src/ArduinoJson/Deserialization/DeserializationError.hpp b/neosensor/libraries/ArduinoJson/src/ArduinoJson/Deserialization/DeserializationError.hpp
index 96b3a178..1bfc3939 100644
--- a/neosensor/libraries/ArduinoJson/src/ArduinoJson/Deserialization/DeserializationError.hpp
+++ b/neosensor/libraries/ArduinoJson/src/ArduinoJson/Deserialization/DeserializationError.hpp
@@ -1,21 +1,20 @@
 // ArduinoJson - https://arduinojson.org
-// Copyright © 2014-2022, Benoit BLANCHON
+// Copyright © 2014-2023, Benoit BLANCHON
 // MIT License
 
 #pragma once
 
-#include <ArduinoJson/Misc/SafeBoolIdiom.hpp>
 #include <ArduinoJson/Namespace.hpp>
+#include <ArduinoJson/Polyfills/pgmspace_generic.hpp>
 #include <ArduinoJson/Polyfills/preprocessor.hpp>
-#include <ArduinoJson/Polyfills/static_array.hpp>
 
 #if ARDUINOJSON_ENABLE_STD_STREAM
 #  include <ostream>
 #endif
 
-namespace ARDUINOJSON_NAMESPACE {
+ARDUINOJSON_BEGIN_PUBLIC_NAMESPACE
 
-class DeserializationError : public SafeBoolIdom<DeserializationError> {
+class DeserializationError {
  public:
   enum Code {
     Ok,
@@ -27,68 +26,68 @@ class DeserializationError : public SafeBoolIdom<DeserializationError> {
   };
 
   DeserializationError() {}
-  DeserializationError(Code c) : _code(c) {}
+  DeserializationError(Code c) : code_(c) {}
 
   // Compare with DeserializationError
   friend bool operator==(const DeserializationError& lhs,
                          const DeserializationError& rhs) {
-    return lhs._code == rhs._code;
+    return lhs.code_ == rhs.code_;
   }
   friend bool operator!=(const DeserializationError& lhs,
                          const DeserializationError& rhs) {
-    return lhs._code != rhs._code;
+    return lhs.code_ != rhs.code_;
   }
 
   // Compare with Code
   friend bool operator==(const DeserializationError& lhs, Code rhs) {
-    return lhs._code == rhs;
+    return lhs.code_ == rhs;
   }
   friend bool operator==(Code lhs, const DeserializationError& rhs) {
-    return lhs == rhs._code;
+    return lhs == rhs.code_;
   }
   friend bool operator!=(const DeserializationError& lhs, Code rhs) {
-    return lhs._code != rhs;
+    return lhs.code_ != rhs;
   }
   friend bool operator!=(Code lhs, const DeserializationError& rhs) {
-    return lhs != rhs._code;
+    return lhs != rhs.code_;
   }
 
-  // Behaves like a bool
-  operator bool_type() const {
-    return _code != Ok ? safe_true() : safe_false();
+  // Returns true if there is an error
+  explicit operator bool() const {
+    return code_ != Ok;
   }
 
   // Returns internal enum, useful for switch statement
   Code code() const {
-    return _code;
+    return code_;
   }
 
   const char* c_str() const {
     static const char* messages[] = {
         "Ok",           "EmptyInput", "IncompleteInput",
         "InvalidInput", "NoMemory",   "TooDeep"};
-    ARDUINOJSON_ASSERT(static_cast<size_t>(_code) <
+    ARDUINOJSON_ASSERT(static_cast<size_t>(code_) <
                        sizeof(messages) / sizeof(messages[0]));
-    return messages[_code];
+    return messages[code_];
   }
 
 #if ARDUINOJSON_ENABLE_PROGMEM
   const __FlashStringHelper* f_str() const {
-    ARDUINOJSON_DEFINE_STATIC_ARRAY(char, s0, "Ok");
-    ARDUINOJSON_DEFINE_STATIC_ARRAY(char, s1, "EmptyInput");
-    ARDUINOJSON_DEFINE_STATIC_ARRAY(char, s2, "IncompleteInput");
-    ARDUINOJSON_DEFINE_STATIC_ARRAY(char, s3, "InvalidInput");
-    ARDUINOJSON_DEFINE_STATIC_ARRAY(char, s4, "NoMemory");
-    ARDUINOJSON_DEFINE_STATIC_ARRAY(char, s5, "TooDeep");
-    ARDUINOJSON_DEFINE_STATIC_ARRAY(
-        const char*, messages, ARDUINOJSON_EXPAND6({s0, s1, s2, s3, s4, s5}));
-    return ARDUINOJSON_READ_STATIC_ARRAY(const __FlashStringHelper*, messages,
-                                         _code);
+    ARDUINOJSON_DEFINE_PROGMEM_ARRAY(char, s0, "Ok");
+    ARDUINOJSON_DEFINE_PROGMEM_ARRAY(char, s1, "EmptyInput");
+    ARDUINOJSON_DEFINE_PROGMEM_ARRAY(char, s2, "IncompleteInput");
+    ARDUINOJSON_DEFINE_PROGMEM_ARRAY(char, s3, "InvalidInput");
+    ARDUINOJSON_DEFINE_PROGMEM_ARRAY(char, s4, "NoMemory");
+    ARDUINOJSON_DEFINE_PROGMEM_ARRAY(char, s5, "TooDeep");
+    ARDUINOJSON_DEFINE_PROGMEM_ARRAY(const char*, messages,
+                                     {s0, s1, s2, s3, s4, s5});
+    return reinterpret_cast<const __FlashStringHelper*>(
+        detail::pgm_read(messages + code_));
   }
 #endif
 
  private:
-  Code _code;
+  Code code_;
 };
 
 #if ARDUINOJSON_ENABLE_STD_STREAM
@@ -104,4 +103,4 @@ inline std::ostream& operator<<(std::ostream& s, DeserializationError::Code c) {
 }
 #endif
 
-}  // namespace ARDUINOJSON_NAMESPACE
+ARDUINOJSON_END_PUBLIC_NAMESPACE
diff --git a/neosensor/libraries/ArduinoJson/src/ArduinoJson/Deserialization/DeserializationOptions.hpp b/neosensor/libraries/ArduinoJson/src/ArduinoJson/Deserialization/DeserializationOptions.hpp
new file mode 100644
index 00000000..8400cc0b
--- /dev/null
+++ b/neosensor/libraries/ArduinoJson/src/ArduinoJson/Deserialization/DeserializationOptions.hpp
@@ -0,0 +1,35 @@
+// ArduinoJson - https://arduinojson.org
+// Copyright © 2014-2023, Benoit BLANCHON
+// MIT License
+
+#pragma once
+
+#include <ArduinoJson/Deserialization/Filter.hpp>
+#include <ArduinoJson/Deserialization/NestingLimit.hpp>
+
+ARDUINOJSON_BEGIN_PRIVATE_NAMESPACE
+
+template <typename TFilter>
+struct DeserializationOptions {
+  TFilter filter;
+  DeserializationOption::NestingLimit nestingLimit;
+};
+
+template <typename TFilter>
+inline DeserializationOptions<TFilter> makeDeserializationOptions(
+    TFilter filter, DeserializationOption::NestingLimit nestingLimit = {}) {
+  return {filter, nestingLimit};
+}
+
+template <typename TFilter>
+inline DeserializationOptions<TFilter> makeDeserializationOptions(
+    DeserializationOption::NestingLimit nestingLimit, TFilter filter) {
+  return {filter, nestingLimit};
+}
+
+inline DeserializationOptions<AllowAllFilter> makeDeserializationOptions(
+    DeserializationOption::NestingLimit nestingLimit = {}) {
+  return {{}, nestingLimit};
+}
+
+ARDUINOJSON_END_PRIVATE_NAMESPACE
diff --git a/neosensor/libraries/ArduinoJson/src/ArduinoJson/Deserialization/Filter.hpp b/neosensor/libraries/ArduinoJson/src/ArduinoJson/Deserialization/Filter.hpp
index 501acf3e..39883027 100644
--- a/neosensor/libraries/ArduinoJson/src/ArduinoJson/Deserialization/Filter.hpp
+++ b/neosensor/libraries/ArduinoJson/src/ArduinoJson/Deserialization/Filter.hpp
@@ -1,45 +1,48 @@
 // ArduinoJson - https://arduinojson.org
-// Copyright © 2014-2022, Benoit BLANCHON
+// Copyright © 2014-2023, Benoit BLANCHON
 // MIT License
 
 #pragma once
 
 #include <ArduinoJson/Namespace.hpp>
 
-namespace ARDUINOJSON_NAMESPACE {
+ARDUINOJSON_BEGIN_PUBLIC_NAMESPACE
 
+namespace DeserializationOption {
 class Filter {
  public:
-  explicit Filter(JsonVariantConst v) : _variant(v) {}
+  explicit Filter(JsonVariantConst v) : variant_(v) {}
 
   bool allow() const {
-    return _variant;
+    return variant_;
   }
 
   bool allowArray() const {
-    return _variant == true || _variant.is<JsonArrayConst>();
+    return variant_ == true || variant_.is<JsonArrayConst>();
   }
 
   bool allowObject() const {
-    return _variant == true || _variant.is<JsonObjectConst>();
+    return variant_ == true || variant_.is<JsonObjectConst>();
   }
 
   bool allowValue() const {
-    return _variant == true;
+    return variant_ == true;
   }
 
   template <typename TKey>
   Filter operator[](const TKey& key) const {
-    if (_variant == true)  // "true" means "allow recursively"
+    if (variant_ == true)  // "true" means "allow recursively"
       return *this;
-    JsonVariantConst member = _variant[key];
-    return Filter(member.isNull() ? _variant["*"] : member);
+    JsonVariantConst member = variant_[key];
+    return Filter(member.isNull() ? variant_["*"] : member);
   }
 
  private:
-  JsonVariantConst _variant;
+  JsonVariantConst variant_;
 };
+}  // namespace DeserializationOption
 
+namespace detail {
 struct AllowAllFilter {
   bool allow() const {
     return true;
@@ -62,5 +65,6 @@ struct AllowAllFilter {
     return AllowAllFilter();
   }
 };
+}  // namespace detail
 
-}  // namespace ARDUINOJSON_NAMESPACE
+ARDUINOJSON_END_PUBLIC_NAMESPACE
diff --git a/neosensor/libraries/ArduinoJson/src/ArduinoJson/Deserialization/NestingLimit.hpp b/neosensor/libraries/ArduinoJson/src/ArduinoJson/Deserialization/NestingLimit.hpp
index af5724d0..6434275b 100644
--- a/neosensor/libraries/ArduinoJson/src/ArduinoJson/Deserialization/NestingLimit.hpp
+++ b/neosensor/libraries/ArduinoJson/src/ArduinoJson/Deserialization/NestingLimit.hpp
@@ -1,5 +1,5 @@
 // ArduinoJson - https://arduinojson.org
-// Copyright © 2014-2022, Benoit BLANCHON
+// Copyright © 2014-2023, Benoit BLANCHON
 // MIT License
 
 #pragma once
@@ -7,23 +7,26 @@
 #include <ArduinoJson/Namespace.hpp>
 #include <ArduinoJson/Polyfills/assert.hpp>
 
-namespace ARDUINOJSON_NAMESPACE {
+ARDUINOJSON_BEGIN_PUBLIC_NAMESPACE
 
+namespace DeserializationOption {
 class NestingLimit {
  public:
-  NestingLimit() : _value(ARDUINOJSON_DEFAULT_NESTING_LIMIT) {}
-  explicit NestingLimit(uint8_t n) : _value(n) {}
+  NestingLimit() : value_(ARDUINOJSON_DEFAULT_NESTING_LIMIT) {}
+  explicit NestingLimit(uint8_t n) : value_(n) {}
 
   NestingLimit decrement() const {
-    ARDUINOJSON_ASSERT(_value > 0);
-    return NestingLimit(static_cast<uint8_t>(_value - 1));
+    ARDUINOJSON_ASSERT(value_ > 0);
+    return NestingLimit(static_cast<uint8_t>(value_ - 1));
   }
 
   bool reached() const {
-    return _value == 0;
+    return value_ == 0;
   }
 
  private:
-  uint8_t _value;
+  uint8_t value_;
 };
-}  // namespace ARDUINOJSON_NAMESPACE
+}  // namespace DeserializationOption
+
+ARDUINOJSON_END_PUBLIC_NAMESPACE
diff --git a/neosensor/libraries/ArduinoJson/src/ArduinoJson/Deserialization/Reader.hpp b/neosensor/libraries/ArduinoJson/src/ArduinoJson/Deserialization/Reader.hpp
index 4e1958c7..5f18c7b3 100644
--- a/neosensor/libraries/ArduinoJson/src/ArduinoJson/Deserialization/Reader.hpp
+++ b/neosensor/libraries/ArduinoJson/src/ArduinoJson/Deserialization/Reader.hpp
@@ -1,31 +1,32 @@
 // ArduinoJson - https://arduinojson.org
-// Copyright © 2014-2022, Benoit BLANCHON
+// Copyright © 2014-2023, Benoit BLANCHON
 // MIT License
 
 #pragma once
 
 #include <ArduinoJson/Namespace.hpp>
+#include <ArduinoJson/Polyfills/utility.hpp>
 
 #include <stdlib.h>  // for size_t
 
-namespace ARDUINOJSON_NAMESPACE {
+ARDUINOJSON_BEGIN_PRIVATE_NAMESPACE
 
 // The default reader is a simple wrapper for Readers that are not copiable
 template <typename TSource, typename Enable = void>
 struct Reader {
  public:
-  Reader(TSource& source) : _source(&source) {}
+  Reader(TSource& source) : source_(&source) {}
 
   int read() {
-    return _source->read();  // Error here? You passed an unsupported input type
+    return source_->read();  // Error here? You passed an unsupported input type
   }
 
   size_t readBytes(char* buffer, size_t length) {
-    return _source->readBytes(buffer, length);
+    return source_->readBytes(buffer, length);
   }
 
  private:
-  TSource* _source;
+  TSource* source_;
 };
 
 template <typename TSource, typename Enable = void>
@@ -33,7 +34,8 @@ struct BoundedReader {
   // no default implementation because we need to pass the size to the
   // constructor
 };
-}  // namespace ARDUINOJSON_NAMESPACE
+
+ARDUINOJSON_END_PRIVATE_NAMESPACE
 
 #include <ArduinoJson/Deserialization/Readers/IteratorReader.hpp>
 #include <ArduinoJson/Deserialization/Readers/RamReader.hpp>
@@ -54,3 +56,18 @@ struct BoundedReader {
 #if ARDUINOJSON_ENABLE_STD_STREAM
 #  include <ArduinoJson/Deserialization/Readers/StdStreamReader.hpp>
 #endif
+
+ARDUINOJSON_BEGIN_PRIVATE_NAMESPACE
+
+template <typename TInput>
+Reader<typename remove_reference<TInput>::type> makeReader(TInput&& input) {
+  return Reader<typename remove_reference<TInput>::type>{
+      detail::forward<TInput>(input)};
+}
+
+template <typename TChar>
+BoundedReader<TChar*> makeReader(TChar* input, size_t inputSize) {
+  return BoundedReader<TChar*>{input, inputSize};
+}
+
+ARDUINOJSON_END_PRIVATE_NAMESPACE
diff --git a/neosensor/libraries/ArduinoJson/src/ArduinoJson/Deserialization/Readers/ArduinoStreamReader.hpp b/neosensor/libraries/ArduinoJson/src/ArduinoJson/Deserialization/Readers/ArduinoStreamReader.hpp
index 179e3768..8a873885 100644
--- a/neosensor/libraries/ArduinoJson/src/ArduinoJson/Deserialization/Readers/ArduinoStreamReader.hpp
+++ b/neosensor/libraries/ArduinoJson/src/ArduinoJson/Deserialization/Readers/ArduinoStreamReader.hpp
@@ -1,31 +1,31 @@
 // ArduinoJson - https://arduinojson.org
-// Copyright © 2014-2022, Benoit BLANCHON
+// Copyright © 2014-2023, Benoit BLANCHON
 // MIT License
 
 #pragma once
 
 #include <Arduino.h>
 
-namespace ARDUINOJSON_NAMESPACE {
+ARDUINOJSON_BEGIN_PRIVATE_NAMESPACE
 
 template <typename TSource>
 struct Reader<TSource,
               typename enable_if<is_base_of<Stream, TSource>::value>::type> {
  public:
-  explicit Reader(Stream& stream) : _stream(&stream) {}
+  explicit Reader(Stream& stream) : stream_(&stream) {}
 
   int read() {
-    // don't use _stream.read() as it ignores the timeout
+    // don't use stream_.read() as it ignores the timeout
     char c;
-    return _stream->readBytes(&c, 1) ? static_cast<unsigned char>(c) : -1;
+    return stream_->readBytes(&c, 1) ? static_cast<unsigned char>(c) : -1;
   }
 
   size_t readBytes(char* buffer, size_t length) {
-    return _stream->readBytes(buffer, length);
+    return stream_->readBytes(buffer, length);
   }
 
  private:
-  Stream* _stream;
+  Stream* stream_;
 };
 
-}  // namespace ARDUINOJSON_NAMESPACE
+ARDUINOJSON_END_PRIVATE_NAMESPACE
diff --git a/neosensor/libraries/ArduinoJson/src/ArduinoJson/Deserialization/Readers/ArduinoStringReader.hpp b/neosensor/libraries/ArduinoJson/src/ArduinoJson/Deserialization/Readers/ArduinoStringReader.hpp
index 652409a0..14491f44 100644
--- a/neosensor/libraries/ArduinoJson/src/ArduinoJson/Deserialization/Readers/ArduinoStringReader.hpp
+++ b/neosensor/libraries/ArduinoJson/src/ArduinoJson/Deserialization/Readers/ArduinoStringReader.hpp
@@ -1,19 +1,19 @@
 // ArduinoJson - https://arduinojson.org
-// Copyright © 2014-2022, Benoit BLANCHON
+// Copyright © 2014-2023, Benoit BLANCHON
 // MIT License
 
 #pragma once
 
 #include <Arduino.h>
 
-namespace ARDUINOJSON_NAMESPACE {
+ARDUINOJSON_BEGIN_PRIVATE_NAMESPACE
 
 template <typename TSource>
 struct Reader<TSource,
-              typename enable_if<is_base_of< ::String, TSource>::value>::type>
+              typename enable_if<is_base_of<::String, TSource>::value>::type>
     : BoundedReader<const char*> {
   explicit Reader(const ::String& s)
       : BoundedReader<const char*>(s.c_str(), s.length()) {}
 };
 
-}  // namespace ARDUINOJSON_NAMESPACE
+ARDUINOJSON_END_PRIVATE_NAMESPACE
diff --git a/neosensor/libraries/ArduinoJson/src/ArduinoJson/Deserialization/Readers/FlashReader.hpp b/neosensor/libraries/ArduinoJson/src/ArduinoJson/Deserialization/Readers/FlashReader.hpp
index c48248c1..97714afb 100644
--- a/neosensor/libraries/ArduinoJson/src/ArduinoJson/Deserialization/Readers/FlashReader.hpp
+++ b/neosensor/libraries/ArduinoJson/src/ArduinoJson/Deserialization/Readers/FlashReader.hpp
@@ -1,55 +1,56 @@
 // ArduinoJson - https://arduinojson.org
-// Copyright © 2014-2022, Benoit BLANCHON
+// Copyright © 2014-2023, Benoit BLANCHON
 // MIT License
 
 #pragma once
 
-#include <Arduino.h>
+#include <ArduinoJson/Polyfills/pgmspace.hpp>
 
-namespace ARDUINOJSON_NAMESPACE {
+ARDUINOJSON_BEGIN_PRIVATE_NAMESPACE
 
 template <>
 struct Reader<const __FlashStringHelper*, void> {
-  const char* _ptr;
+  const char* ptr_;
 
  public:
   explicit Reader(const __FlashStringHelper* ptr)
-      : _ptr(reinterpret_cast<const char*>(ptr)) {}
+      : ptr_(reinterpret_cast<const char*>(ptr)) {}
 
   int read() {
-    return pgm_read_byte(_ptr++);
+    return pgm_read_byte(ptr_++);
   }
 
   size_t readBytes(char* buffer, size_t length) {
-    memcpy_P(buffer, _ptr, length);
-    _ptr += length;
+    memcpy_P(buffer, ptr_, length);
+    ptr_ += length;
     return length;
   }
 };
 
 template <>
 struct BoundedReader<const __FlashStringHelper*, void> {
-  const char* _ptr;
-  const char* _end;
+  const char* ptr_;
+  const char* end_;
 
  public:
   explicit BoundedReader(const __FlashStringHelper* ptr, size_t size)
-      : _ptr(reinterpret_cast<const char*>(ptr)), _end(_ptr + size) {}
+      : ptr_(reinterpret_cast<const char*>(ptr)), end_(ptr_ + size) {}
 
   int read() {
-    if (_ptr < _end)
-      return pgm_read_byte(_ptr++);
+    if (ptr_ < end_)
+      return pgm_read_byte(ptr_++);
     else
       return -1;
   }
 
   size_t readBytes(char* buffer, size_t length) {
-    size_t available = static_cast<size_t>(_end - _ptr);
+    size_t available = static_cast<size_t>(end_ - ptr_);
     if (available < length)
       length = available;
-    memcpy_P(buffer, _ptr, length);
-    _ptr += length;
+    memcpy_P(buffer, ptr_, length);
+    ptr_ += length;
     return length;
   }
 };
-}  // namespace ARDUINOJSON_NAMESPACE
+
+ARDUINOJSON_END_PRIVATE_NAMESPACE
diff --git a/neosensor/libraries/ArduinoJson/src/ArduinoJson/Deserialization/Readers/IteratorReader.hpp b/neosensor/libraries/ArduinoJson/src/ArduinoJson/Deserialization/Readers/IteratorReader.hpp
index 96494df0..c0ca4a77 100644
--- a/neosensor/libraries/ArduinoJson/src/ArduinoJson/Deserialization/Readers/IteratorReader.hpp
+++ b/neosensor/libraries/ArduinoJson/src/ArduinoJson/Deserialization/Readers/IteratorReader.hpp
@@ -1,30 +1,30 @@
 // ArduinoJson - https://arduinojson.org
-// Copyright © 2014-2022, Benoit BLANCHON
+// Copyright © 2014-2023, Benoit BLANCHON
 // MIT License
 
 #pragma once
 
-namespace ARDUINOJSON_NAMESPACE {
+ARDUINOJSON_BEGIN_PRIVATE_NAMESPACE
 
 template <typename TIterator>
 class IteratorReader {
-  TIterator _ptr, _end;
+  TIterator ptr_, end_;
 
  public:
   explicit IteratorReader(TIterator begin, TIterator end)
-      : _ptr(begin), _end(end) {}
+      : ptr_(begin), end_(end) {}
 
   int read() {
-    if (_ptr < _end)
-      return static_cast<unsigned char>(*_ptr++);
+    if (ptr_ < end_)
+      return static_cast<unsigned char>(*ptr_++);
     else
       return -1;
   }
 
   size_t readBytes(char* buffer, size_t length) {
     size_t i = 0;
-    while (i < length && _ptr < _end)
-      buffer[i++] = *_ptr++;
+    while (i < length && ptr_ < end_)
+      buffer[i++] = *ptr_++;
     return i;
   }
 };
@@ -41,4 +41,5 @@ struct Reader<TSource, typename void_<typename TSource::const_iterator>::type>
       : IteratorReader<typename TSource::const_iterator>(source.begin(),
                                                          source.end()) {}
 };
-}  // namespace ARDUINOJSON_NAMESPACE
+
+ARDUINOJSON_END_PRIVATE_NAMESPACE
diff --git a/neosensor/libraries/ArduinoJson/src/ArduinoJson/Deserialization/Readers/RamReader.hpp b/neosensor/libraries/ArduinoJson/src/ArduinoJson/Deserialization/Readers/RamReader.hpp
index 120ce8a8..eff67bac 100644
--- a/neosensor/libraries/ArduinoJson/src/ArduinoJson/Deserialization/Readers/RamReader.hpp
+++ b/neosensor/libraries/ArduinoJson/src/ArduinoJson/Deserialization/Readers/RamReader.hpp
@@ -1,12 +1,12 @@
 // ArduinoJson - https://arduinojson.org
-// Copyright © 2014-2022, Benoit BLANCHON
+// Copyright © 2014-2023, Benoit BLANCHON
 // MIT License
 
 #pragma once
 
 #include <ArduinoJson/Polyfills/type_traits.hpp>
 
-namespace ARDUINOJSON_NAMESPACE {
+ARDUINOJSON_BEGIN_PRIVATE_NAMESPACE
 
 template <typename T>
 struct IsCharOrVoid {
@@ -21,19 +21,19 @@ struct IsCharOrVoid<const T> : IsCharOrVoid<T> {};
 template <typename TSource>
 struct Reader<TSource*,
               typename enable_if<IsCharOrVoid<TSource>::value>::type> {
-  const char* _ptr;
+  const char* ptr_;
 
  public:
   explicit Reader(const void* ptr)
-      : _ptr(ptr ? reinterpret_cast<const char*>(ptr) : "") {}
+      : ptr_(ptr ? reinterpret_cast<const char*>(ptr) : "") {}
 
   int read() {
-    return static_cast<unsigned char>(*_ptr++);
+    return static_cast<unsigned char>(*ptr_++);
   }
 
   size_t readBytes(char* buffer, size_t length) {
     for (size_t i = 0; i < length; i++)
-      buffer[i] = *_ptr++;
+      buffer[i] = *ptr_++;
     return length;
   }
 };
@@ -48,4 +48,4 @@ struct BoundedReader<TSource*,
                                     reinterpret_cast<const char*>(ptr) + len) {}
 };
 
-}  // namespace ARDUINOJSON_NAMESPACE
+ARDUINOJSON_END_PRIVATE_NAMESPACE
diff --git a/neosensor/libraries/ArduinoJson/src/ArduinoJson/Deserialization/Readers/StdStreamReader.hpp b/neosensor/libraries/ArduinoJson/src/ArduinoJson/Deserialization/Readers/StdStreamReader.hpp
index c92f7931..41e0c00a 100644
--- a/neosensor/libraries/ArduinoJson/src/ArduinoJson/Deserialization/Readers/StdStreamReader.hpp
+++ b/neosensor/libraries/ArduinoJson/src/ArduinoJson/Deserialization/Readers/StdStreamReader.hpp
@@ -1,29 +1,30 @@
 // ArduinoJson - https://arduinojson.org
-// Copyright © 2014-2022, Benoit BLANCHON
+// Copyright © 2014-2023, Benoit BLANCHON
 // MIT License
 
 #pragma once
 
 #include <istream>
 
-namespace ARDUINOJSON_NAMESPACE {
+ARDUINOJSON_BEGIN_PRIVATE_NAMESPACE
 
 template <typename TSource>
 struct Reader<TSource, typename enable_if<
                            is_base_of<std::istream, TSource>::value>::type> {
  public:
-  explicit Reader(std::istream& stream) : _stream(&stream) {}
+  explicit Reader(std::istream& stream) : stream_(&stream) {}
 
   int read() {
-    return _stream->get();
+    return stream_->get();
   }
 
   size_t readBytes(char* buffer, size_t length) {
-    _stream->read(buffer, static_cast<std::streamsize>(length));
-    return static_cast<size_t>(_stream->gcount());
+    stream_->read(buffer, static_cast<std::streamsize>(length));
+    return static_cast<size_t>(stream_->gcount());
   }
 
  private:
-  std::istream* _stream;
+  std::istream* stream_;
 };
-}  // namespace ARDUINOJSON_NAMESPACE
+
+ARDUINOJSON_END_PRIVATE_NAMESPACE
diff --git a/neosensor/libraries/ArduinoJson/src/ArduinoJson/Deserialization/Readers/VariantReader.hpp b/neosensor/libraries/ArduinoJson/src/ArduinoJson/Deserialization/Readers/VariantReader.hpp
index 17918cd0..b60f1cec 100644
--- a/neosensor/libraries/ArduinoJson/src/ArduinoJson/Deserialization/Readers/VariantReader.hpp
+++ b/neosensor/libraries/ArduinoJson/src/ArduinoJson/Deserialization/Readers/VariantReader.hpp
@@ -1,5 +1,5 @@
 // ArduinoJson - https://arduinojson.org
-// Copyright © 2014-2022, Benoit BLANCHON
+// Copyright © 2014-2023, Benoit BLANCHON
 // MIT License
 
 #pragma once
@@ -7,7 +7,7 @@
 #include <ArduinoJson/Object/MemberProxy.hpp>
 #include <ArduinoJson/Variant/JsonVariantConst.hpp>
 
-namespace ARDUINOJSON_NAMESPACE {
+ARDUINOJSON_BEGIN_PRIVATE_NAMESPACE
 
 template <typename TVariant>
 struct Reader<TVariant, typename enable_if<IsVariant<TVariant>::value>::type>
@@ -16,4 +16,4 @@ struct Reader<TVariant, typename enable_if<IsVariant<TVariant>::value>::type>
       : Reader<char*, void>(x.template as<const char*>()) {}
 };
 
-}  // namespace ARDUINOJSON_NAMESPACE
+ARDUINOJSON_END_PRIVATE_NAMESPACE
diff --git a/neosensor/libraries/ArduinoJson/src/ArduinoJson/Deserialization/deserialize.hpp b/neosensor/libraries/ArduinoJson/src/ArduinoJson/Deserialization/deserialize.hpp
index 062b1092..9f4d78e8 100644
--- a/neosensor/libraries/ArduinoJson/src/ArduinoJson/Deserialization/deserialize.hpp
+++ b/neosensor/libraries/ArduinoJson/src/ArduinoJson/Deserialization/deserialize.hpp
@@ -1,16 +1,27 @@
 // ArduinoJson - https://arduinojson.org
-// Copyright © 2014-2022, Benoit BLANCHON
+// Copyright © 2014-2023, Benoit BLANCHON
 // MIT License
 
 #pragma once
 
 #include <ArduinoJson/Deserialization/DeserializationError.hpp>
-#include <ArduinoJson/Deserialization/Filter.hpp>
-#include <ArduinoJson/Deserialization/NestingLimit.hpp>
+#include <ArduinoJson/Deserialization/DeserializationOptions.hpp>
 #include <ArduinoJson/Deserialization/Reader.hpp>
+#include <ArduinoJson/Polyfills/utility.hpp>
 #include <ArduinoJson/StringStorage/StringStorage.hpp>
 
-namespace ARDUINOJSON_NAMESPACE {
+ARDUINOJSON_BEGIN_PRIVATE_NAMESPACE
+
+// A meta-function that returns the first type of the parameter pack
+// or void if empty
+template <typename...>
+struct first_or_void {
+  using type = void;
+};
+template <typename T, typename... Rest>
+struct first_or_void<T, Rest...> {
+  using type = T;
+};
 
 template <template <typename, typename> class TDeserializer, typename TReader,
           typename TWriter>
@@ -21,55 +32,35 @@ TDeserializer<TReader, TWriter> makeDeserializer(MemoryPool* pool,
   return TDeserializer<TReader, TWriter>(pool, reader, writer);
 }
 
-// deserialize(JsonDocument&, const std::string&, NestingLimit, Filter);
-// deserialize(JsonDocument&, const String&, NestingLimit, Filter);
-// deserialize(JsonDocument&, char*, NestingLimit, Filter);
-// deserialize(JsonDocument&, const char*, NestingLimit, Filter);
-// deserialize(JsonDocument&, const __FlashStringHelper*, NestingLimit, Filter);
-template <template <typename, typename> class TDeserializer, typename TString,
-          typename TFilter>
-typename enable_if<!is_array<TString>::value, DeserializationError>::type
-deserialize(JsonDocument& doc, const TString& input, NestingLimit nestingLimit,
-            TFilter filter) {
-  Reader<TString> reader(input);
-  VariantData* data = VariantAttorney::getData(doc);
-  MemoryPool* pool = VariantAttorney::getPool(doc);
+template <template <typename, typename> class TDeserializer, typename TStream,
+          typename... Args,
+          typename = typename enable_if<  // issue #1897
+              !is_integral<typename first_or_void<Args...>::type>::value>::type>
+DeserializationError deserialize(JsonDocument& doc, TStream&& input,
+                                 Args... args) {
+  auto reader = makeReader(detail::forward<TStream>(input));
+  auto data = VariantAttorney::getData(doc);
+  auto pool = VariantAttorney::getPool(doc);
+  auto options = makeDeserializationOptions(args...);
   doc.clear();
   return makeDeserializer<TDeserializer>(pool, reader,
                                          makeStringStorage(input, pool))
-      .parse(*data, filter, nestingLimit);
+      .parse(*data, options.filter, options.nestingLimit);
 }
-//
-// deserialize(JsonDocument&, char*, size_t, NestingLimit, Filter);
-// deserialize(JsonDocument&, const char*, size_t, NestingLimit, Filter);
-// deserialize(JsonDocument&, const __FlashStringHelper*, size_t, NL, Filter);
+
 template <template <typename, typename> class TDeserializer, typename TChar,
-          typename TFilter>
+          typename Size, typename... Args,
+          typename = typename enable_if<is_integral<Size>::value>::type>
 DeserializationError deserialize(JsonDocument& doc, TChar* input,
-                                 size_t inputSize, NestingLimit nestingLimit,
-                                 TFilter filter) {
-  BoundedReader<TChar*> reader(input, inputSize);
-  VariantData* data = VariantAttorney::getData(doc);
-  MemoryPool* pool = VariantAttorney::getPool(doc);
-  doc.clear();
-  return makeDeserializer<TDeserializer>(pool, reader,
-                                         makeStringStorage(input, pool))
-      .parse(*data, filter, nestingLimit);
-}
-//
-// deserialize(JsonDocument&, std::istream&, NestingLimit, Filter);
-// deserialize(JsonDocument&, Stream&, NestingLimit, Filter);
-template <template <typename, typename> class TDeserializer, typename TStream,
-          typename TFilter>
-DeserializationError deserialize(JsonDocument& doc, TStream& input,
-                                 NestingLimit nestingLimit, TFilter filter) {
-  Reader<TStream> reader(input);
-  VariantData* data = VariantAttorney::getData(doc);
-  MemoryPool* pool = VariantAttorney::getPool(doc);
+                                 Size inputSize, Args... args) {
+  auto reader = makeReader(input, size_t(inputSize));
+  auto data = VariantAttorney::getData(doc);
+  auto pool = VariantAttorney::getPool(doc);
+  auto options = makeDeserializationOptions(args...);
   doc.clear();
   return makeDeserializer<TDeserializer>(pool, reader,
                                          makeStringStorage(input, pool))
-      .parse(*data, filter, nestingLimit);
+      .parse(*data, options.filter, options.nestingLimit);
 }
 
-}  // namespace ARDUINOJSON_NAMESPACE
+ARDUINOJSON_END_PRIVATE_NAMESPACE
diff --git a/neosensor/libraries/ArduinoJson/src/ArduinoJson/Document/BasicJsonDocument.hpp b/neosensor/libraries/ArduinoJson/src/ArduinoJson/Document/BasicJsonDocument.hpp
index 7a32f6f2..bc206f02 100644
--- a/neosensor/libraries/ArduinoJson/src/ArduinoJson/Document/BasicJsonDocument.hpp
+++ b/neosensor/libraries/ArduinoJson/src/ArduinoJson/Document/BasicJsonDocument.hpp
@@ -1,12 +1,12 @@
 // ArduinoJson - https://arduinojson.org
-// Copyright © 2014-2022, Benoit BLANCHON
+// Copyright © 2014-2023, Benoit BLANCHON
 // MIT License
 
 #pragma once
 
 #include <ArduinoJson/Document/JsonDocument.hpp>
 
-namespace ARDUINOJSON_NAMESPACE {
+ARDUINOJSON_BEGIN_PUBLIC_NAMESPACE
 
 // Helper to implement the "base-from-member" idiom
 // (we need to store the allocator before constructing JsonDocument)
@@ -14,27 +14,27 @@ template <typename TAllocator>
 class AllocatorOwner {
  public:
   AllocatorOwner() {}
-  AllocatorOwner(TAllocator a) : _allocator(a) {}
+  AllocatorOwner(TAllocator a) : allocator_(a) {}
 
   void* allocate(size_t size) {
-    return _allocator.allocate(size);
+    return allocator_.allocate(size);
   }
 
   void deallocate(void* ptr) {
     if (ptr)
-      _allocator.deallocate(ptr);
+      allocator_.deallocate(ptr);
   }
 
   void* reallocate(void* ptr, size_t new_size) {
-    return _allocator.reallocate(ptr, new_size);
+    return allocator_.reallocate(ptr, new_size);
   }
 
   TAllocator& allocator() {
-    return _allocator;
+    return allocator_;
   }
 
  private:
-  TAllocator _allocator;
+  TAllocator allocator_;
 };
 
 // A JsonDocument that uses the provided allocator to allocate its memory pool.
@@ -52,11 +52,9 @@ class BasicJsonDocument : AllocatorOwner<TAllocator>, public JsonDocument {
   }
 
   // Move-constructor
-#if ARDUINOJSON_HAS_RVALUE_REFERENCES
   BasicJsonDocument(BasicJsonDocument&& src) : AllocatorOwner<TAllocator>(src) {
     moveAssignFrom(src);
   }
-#endif
 
   BasicJsonDocument(const JsonDocument& src) {
     copyAssignFrom(src);
@@ -64,13 +62,14 @@ class BasicJsonDocument : AllocatorOwner<TAllocator>, public JsonDocument {
 
   // Construct from variant, array, or object
   template <typename T>
-  BasicJsonDocument(
-      const T& src,
-      typename enable_if<
-          is_same<T, JsonVariant>::value ||
-          is_same<T, JsonVariantConst>::value || is_same<T, JsonArray>::value ||
-          is_same<T, JsonArrayConst>::value || is_same<T, JsonObject>::value ||
-          is_same<T, JsonObjectConst>::value>::type* = 0)
+  BasicJsonDocument(const T& src,
+                    typename detail::enable_if<
+                        detail::is_same<T, JsonVariant>::value ||
+                        detail::is_same<T, JsonVariantConst>::value ||
+                        detail::is_same<T, JsonArray>::value ||
+                        detail::is_same<T, JsonArrayConst>::value ||
+                        detail::is_same<T, JsonObject>::value ||
+                        detail::is_same<T, JsonObjectConst>::value>::type* = 0)
       : JsonDocument(allocPool(src.memoryUsage())) {
     set(src);
   }
@@ -90,12 +89,10 @@ class BasicJsonDocument : AllocatorOwner<TAllocator>, public JsonDocument {
     return *this;
   }
 
-#if ARDUINOJSON_HAS_RVALUE_REFERENCES
   BasicJsonDocument& operator=(BasicJsonDocument&& src) {
     moveAssignFrom(src);
     return *this;
   }
-#endif
 
   template <typename T>
   BasicJsonDocument& operator=(const T& src) {
@@ -109,18 +106,18 @@ class BasicJsonDocument : AllocatorOwner<TAllocator>, public JsonDocument {
   // Reduces the capacity of the memory pool to match the current usage.
   // https://arduinojson.org/v6/api/basicjsondocument/shrinktofit/
   void shrinkToFit() {
-    ptrdiff_t bytes_reclaimed = _pool.squash();
+    ptrdiff_t bytes_reclaimed = pool_.squash();
     if (bytes_reclaimed == 0)
       return;
 
-    void* old_ptr = _pool.buffer();
-    void* new_ptr = this->reallocate(old_ptr, _pool.capacity());
+    void* old_ptr = pool_.buffer();
+    void* new_ptr = this->reallocate(old_ptr, pool_.capacity());
 
     ptrdiff_t ptr_offset =
         static_cast<char*>(new_ptr) - static_cast<char*>(old_ptr);
 
-    _pool.movePointers(ptr_offset);
-    _data.movePointers(ptr_offset, ptr_offset - bytes_reclaimed);
+    pool_.movePointers(ptr_offset);
+    data_.movePointers(ptr_offset, ptr_offset - bytes_reclaimed);
   }
 
   // Reclaims the memory leaked when removing and replacing values.
@@ -130,7 +127,6 @@ class BasicJsonDocument : AllocatorOwner<TAllocator>, public JsonDocument {
     BasicJsonDocument tmp(*this);
     if (!tmp.capacity())
       return false;
-    tmp.set(*this);
     moveAssignFrom(tmp);
     return true;
   }
@@ -138,17 +134,17 @@ class BasicJsonDocument : AllocatorOwner<TAllocator>, public JsonDocument {
   using AllocatorOwner<TAllocator>::allocator;
 
  private:
-  MemoryPool allocPool(size_t requiredSize) {
-    size_t capa = addPadding(requiredSize);
-    return MemoryPool(reinterpret_cast<char*>(this->allocate(capa)), capa);
+  detail::MemoryPool allocPool(size_t requiredSize) {
+    size_t capa = detail::addPadding(requiredSize);
+    return {reinterpret_cast<char*>(this->allocate(capa)), capa};
   }
 
   void reallocPool(size_t requiredSize) {
-    size_t capa = addPadding(requiredSize);
-    if (capa == _pool.capacity())
+    size_t capa = detail::addPadding(requiredSize);
+    if (capa == pool_.capacity())
       return;
     freePool();
-    replacePool(allocPool(addPadding(requiredSize)));
+    replacePool(allocPool(detail::addPadding(requiredSize)));
   }
 
   void freePool() {
@@ -162,11 +158,11 @@ class BasicJsonDocument : AllocatorOwner<TAllocator>, public JsonDocument {
 
   void moveAssignFrom(BasicJsonDocument& src) {
     freePool();
-    _data = src._data;
-    _pool = src._pool;
-    src._data.setNull();
-    src._pool = MemoryPool(0, 0);
+    data_ = src.data_;
+    pool_ = src.pool_;
+    src.data_.setNull();
+    src.pool_ = {0, 0};
   }
 };
 
-}  // namespace ARDUINOJSON_NAMESPACE
+ARDUINOJSON_END_PUBLIC_NAMESPACE
diff --git a/neosensor/libraries/ArduinoJson/src/ArduinoJson/Document/DynamicJsonDocument.hpp b/neosensor/libraries/ArduinoJson/src/ArduinoJson/Document/DynamicJsonDocument.hpp
index dd889a81..3c2dc3a6 100644
--- a/neosensor/libraries/ArduinoJson/src/ArduinoJson/Document/DynamicJsonDocument.hpp
+++ b/neosensor/libraries/ArduinoJson/src/ArduinoJson/Document/DynamicJsonDocument.hpp
@@ -1,5 +1,5 @@
 // ArduinoJson - https://arduinojson.org
-// Copyright © 2014-2022, Benoit BLANCHON
+// Copyright © 2014-2023, Benoit BLANCHON
 // MIT License
 
 #pragma once
@@ -8,7 +8,7 @@
 
 #include <stdlib.h>  // malloc, free
 
-namespace ARDUINOJSON_NAMESPACE {
+ARDUINOJSON_BEGIN_PUBLIC_NAMESPACE
 
 // The allocator of DynamicJsonDocument.
 struct DefaultAllocator {
@@ -29,4 +29,4 @@ struct DefaultAllocator {
 // https://arduinojson.org/v6/api/dynamicjsondocument/
 typedef BasicJsonDocument<DefaultAllocator> DynamicJsonDocument;
 
-}  // namespace ARDUINOJSON_NAMESPACE
+ARDUINOJSON_END_PUBLIC_NAMESPACE
diff --git a/neosensor/libraries/ArduinoJson/src/ArduinoJson/Document/JsonDocument.hpp b/neosensor/libraries/ArduinoJson/src/ArduinoJson/Document/JsonDocument.hpp
index 286283f8..39c1536f 100644
--- a/neosensor/libraries/ArduinoJson/src/ArduinoJson/Document/JsonDocument.hpp
+++ b/neosensor/libraries/ArduinoJson/src/ArduinoJson/Document/JsonDocument.hpp
@@ -1,5 +1,5 @@
 // ArduinoJson - https://arduinojson.org
-// Copyright © 2014-2022, Benoit BLANCHON
+// Copyright © 2014-2023, Benoit BLANCHON
 // MIT License
 
 #pragma once
@@ -12,14 +12,17 @@
 #include <ArduinoJson/Variant/JsonVariantConst.hpp>
 #include <ArduinoJson/Variant/VariantTo.hpp>
 
-namespace ARDUINOJSON_NAMESPACE {
+ARDUINOJSON_BEGIN_PUBLIC_NAMESPACE
 
 // A JSON document.
 // https://arduinojson.org/v6/api/jsondocument/
-class JsonDocument : public VariantOperators<const JsonDocument&> {
-  friend class VariantAttorney;
+class JsonDocument : public detail::VariantOperators<const JsonDocument&> {
+  friend class detail::VariantAttorney;
 
  public:
+  JsonDocument(const JsonDocument&) = delete;
+  JsonDocument& operator=(const JsonDocument&) = delete;
+
   // Casts the root to the specified type.
   // https://arduinojson.org/v6/api/jsondocument/as/
   template <typename T>
@@ -37,8 +40,8 @@ class JsonDocument : public VariantOperators<const JsonDocument&> {
   // Empties the document and resets the memory pool
   // https://arduinojson.org/v6/api/jsondocument/clear/
   void clear() {
-    _pool.clear();
-    _data.init();
+    pool_.clear();
+    data_.setNull();
   }
 
   // Returns true if the root is of the specified type.
@@ -64,31 +67,31 @@ class JsonDocument : public VariantOperators<const JsonDocument&> {
   // Returns the number of used bytes in the memory pool.
   // https://arduinojson.org/v6/api/jsondocument/memoryusage/
   size_t memoryUsage() const {
-    return _pool.size();
+    return pool_.size();
   }
 
   // Returns trues if the memory pool was too small.
   // https://arduinojson.org/v6/api/jsondocument/overflowed/
   bool overflowed() const {
-    return _pool.overflowed();
+    return pool_.overflowed();
   }
 
   // Returns the depth (nesting level) of the array.
   // https://arduinojson.org/v6/api/jsondocument/nesting/
   size_t nesting() const {
-    return variantNesting(&_data);
+    return variantNesting(&data_);
   }
 
   // Returns the capacity of the memory pool.
   // https://arduinojson.org/v6/api/jsondocument/capacity/
   size_t capacity() const {
-    return _pool.capacity();
+    return pool_.capacity();
   }
 
   // Returns the number of elements in the root array or object.
   // https://arduinojson.org/v6/api/jsondocument/size/
   size_t size() const {
-    return _data.size();
+    return data_.size();
   }
 
   // Copies the specified document.
@@ -100,15 +103,16 @@ class JsonDocument : public VariantOperators<const JsonDocument&> {
   // Replaces the root with the specified value.
   // https://arduinojson.org/v6/api/jsondocument/set/
   template <typename T>
-  typename enable_if<!is_base_of<JsonDocument, T>::value, bool>::type set(
-      const T& src) {
+  typename detail::enable_if<!detail::is_base_of<JsonDocument, T>::value,
+                             bool>::type
+  set(const T& src) {
     return to<JsonVariant>().set(src);
   }
 
   // Clears the document and converts it to the specified type.
   // https://arduinojson.org/v6/api/jsondocument/to/
   template <typename T>
-  typename VariantTo<T>::type to() {
+  typename detail::VariantTo<T>::type to() {
     clear();
     return getVariant().template to<T>();
   }
@@ -157,69 +161,71 @@ class JsonDocument : public VariantOperators<const JsonDocument&> {
   // https://arduinojson.org/v6/api/jsondocument/containskey/
   template <typename TChar>
   bool containsKey(TChar* key) const {
-    return _data.getMember(adaptString(key)) != 0;
+    return data_.getMember(detail::adaptString(key)) != 0;
   }
 
   // Returns true if the root object contains the specified key.
   // https://arduinojson.org/v6/api/jsondocument/containskey/
   template <typename TString>
   bool containsKey(const TString& key) const {
-    return _data.getMember(adaptString(key)) != 0;
+    return data_.getMember(detail::adaptString(key)) != 0;
   }
 
   // Gets or sets a root object's member.
   // https://arduinojson.org/v6/api/jsondocument/subscript/
   template <typename TString>
-  FORCE_INLINE typename enable_if<IsString<TString>::value,
-                                  MemberProxy<JsonDocument&, TString> >::type
+  FORCE_INLINE typename detail::enable_if<
+      detail::IsString<TString>::value,
+      detail::MemberProxy<JsonDocument&, TString>>::type
   operator[](const TString& key) {
-    return MemberProxy<JsonDocument&, TString>(*this, key);
+    return {*this, key};
   }
 
   // Gets or sets a root object's member.
   // https://arduinojson.org/v6/api/jsondocument/subscript/
   template <typename TChar>
-  FORCE_INLINE typename enable_if<IsString<TChar*>::value,
-                                  MemberProxy<JsonDocument&, TChar*> >::type
+  FORCE_INLINE typename detail::enable_if<
+      detail::IsString<TChar*>::value,
+      detail::MemberProxy<JsonDocument&, TChar*>>::type
   operator[](TChar* key) {
-    return MemberProxy<JsonDocument&, TChar*>(*this, key);
+    return {*this, key};
   }
 
   // Gets a root object's member.
   // https://arduinojson.org/v6/api/jsondocument/subscript/
   template <typename TString>
-  FORCE_INLINE
-      typename enable_if<IsString<TString>::value, JsonVariantConst>::type
-      operator[](const TString& key) const {
-    return JsonVariantConst(_data.getMember(adaptString(key)));
+  FORCE_INLINE typename detail::enable_if<detail::IsString<TString>::value,
+                                          JsonVariantConst>::type
+  operator[](const TString& key) const {
+    return JsonVariantConst(data_.getMember(detail::adaptString(key)));
   }
 
   // Gets a root object's member.
   // https://arduinojson.org/v6/api/jsondocument/subscript/
   template <typename TChar>
-  FORCE_INLINE
-      typename enable_if<IsString<TChar*>::value, JsonVariantConst>::type
-      operator[](TChar* key) const {
-    return JsonVariantConst(_data.getMember(adaptString(key)));
+  FORCE_INLINE typename detail::enable_if<detail::IsString<TChar*>::value,
+                                          JsonVariantConst>::type
+  operator[](TChar* key) const {
+    return JsonVariantConst(data_.getMember(detail::adaptString(key)));
   }
 
   // Gets or sets a root array's element.
   // https://arduinojson.org/v6/api/jsondocument/subscript/
-  FORCE_INLINE ElementProxy<JsonDocument&> operator[](size_t index) {
-    return ElementProxy<JsonDocument&>(*this, index);
+  FORCE_INLINE detail::ElementProxy<JsonDocument&> operator[](size_t index) {
+    return {*this, index};
   }
 
   // Gets a root array's member.
   // https://arduinojson.org/v6/api/jsondocument/subscript/
   FORCE_INLINE JsonVariantConst operator[](size_t index) const {
-    return JsonVariantConst(_data.getElement(index));
+    return JsonVariantConst(data_.getElement(index));
   }
 
   // Appends a new (null) element to the root array.
   // Returns a reference to the new element.
   // https://arduinojson.org/v6/api/jsondocument/add/
   FORCE_INLINE JsonVariant add() {
-    return JsonVariant(&_pool, _data.addElement(&_pool));
+    return JsonVariant(&pool_, data_.addElement(&pool_));
   }
 
   // Appends a value to the root array.
@@ -240,25 +246,26 @@ class JsonDocument : public VariantOperators<const JsonDocument&> {
   // ⚠️ Doesn't release the memory associated with the removed element.
   // https://arduinojson.org/v6/api/jsondocument/remove/
   FORCE_INLINE void remove(size_t index) {
-    _data.remove(index);
+    data_.remove(index);
   }
 
   // Removes a member of the root object.
   // ⚠️ Doesn't release the memory associated with the removed element.
   // https://arduinojson.org/v6/api/jsondocument/remove/
   template <typename TChar>
-  FORCE_INLINE typename enable_if<IsString<TChar*>::value>::type remove(
-      TChar* key) {
-    _data.remove(adaptString(key));
+  FORCE_INLINE typename detail::enable_if<detail::IsString<TChar*>::value>::type
+  remove(TChar* key) {
+    data_.remove(detail::adaptString(key));
   }
 
   // Removes a member of the root object.
   // ⚠️ Doesn't release the memory associated with the removed element.
   // https://arduinojson.org/v6/api/jsondocument/remove/
   template <typename TString>
-  FORCE_INLINE typename enable_if<IsString<TString>::value>::type remove(
-      const TString& key) {
-    _data.remove(adaptString(key));
+  FORCE_INLINE
+      typename detail::enable_if<detail::IsString<TString>::value>::type
+      remove(const TString& key) {
+    data_.remove(detail::adaptString(key));
   }
 
   FORCE_INLINE operator JsonVariant() {
@@ -270,54 +277,44 @@ class JsonDocument : public VariantOperators<const JsonDocument&> {
   }
 
  protected:
-  JsonDocument() : _pool(0, 0) {
-    _data.init();
-  }
+  JsonDocument() : pool_(0, 0) {}
 
-  JsonDocument(MemoryPool pool) : _pool(pool) {
-    _data.init();
-  }
+  JsonDocument(detail::MemoryPool pool) : pool_(pool) {}
 
-  JsonDocument(char* buf, size_t capa) : _pool(buf, capa) {
-    _data.init();
-  }
+  JsonDocument(char* buf, size_t capa) : pool_(buf, capa) {}
 
   ~JsonDocument() {}
 
-  void replacePool(MemoryPool pool) {
-    _pool = pool;
+  void replacePool(detail::MemoryPool pool) {
+    pool_ = pool;
   }
 
   JsonVariant getVariant() {
-    return JsonVariant(&_pool, &_data);
+    return JsonVariant(&pool_, &data_);
   }
 
   JsonVariantConst getVariant() const {
-    return JsonVariantConst(&_data);
+    return JsonVariantConst(&data_);
   }
 
-  MemoryPool _pool;
-  VariantData _data;
-
- private:
-  JsonDocument(const JsonDocument&);
-  JsonDocument& operator=(const JsonDocument&);
+  detail::MemoryPool pool_;
+  detail::VariantData data_;
 
  protected:
-  MemoryPool* getPool() {
-    return &_pool;
+  detail::MemoryPool* getPool() {
+    return &pool_;
   }
 
-  VariantData* getData() {
-    return &_data;
+  detail::VariantData* getData() {
+    return &data_;
   }
 
-  const VariantData* getData() const {
-    return &_data;
+  const detail::VariantData* getData() const {
+    return &data_;
   }
 
-  VariantData* getOrCreateData() {
-    return &_data;
+  detail::VariantData* getOrCreateData() {
+    return &data_;
   }
 };
 
@@ -325,4 +322,4 @@ inline void convertToJson(const JsonDocument& src, JsonVariant dst) {
   dst.set(src.as<JsonVariantConst>());
 }
 
-}  // namespace ARDUINOJSON_NAMESPACE
+ARDUINOJSON_END_PUBLIC_NAMESPACE
diff --git a/neosensor/libraries/ArduinoJson/src/ArduinoJson/Document/StaticJsonDocument.hpp b/neosensor/libraries/ArduinoJson/src/ArduinoJson/Document/StaticJsonDocument.hpp
index d88dc574..07166664 100644
--- a/neosensor/libraries/ArduinoJson/src/ArduinoJson/Document/StaticJsonDocument.hpp
+++ b/neosensor/libraries/ArduinoJson/src/ArduinoJson/Document/StaticJsonDocument.hpp
@@ -1,37 +1,38 @@
 // ArduinoJson - https://arduinojson.org
-// Copyright © 2014-2022, Benoit BLANCHON
+// Copyright © 2014-2023, Benoit BLANCHON
 // MIT License
 
 #pragma once
 
 #include <ArduinoJson/Document/JsonDocument.hpp>
 
-namespace ARDUINOJSON_NAMESPACE {
+ARDUINOJSON_BEGIN_PUBLIC_NAMESPACE
 
 // A JsonDocument with a memory pool on the stack.
 template <size_t desiredCapacity>
 class StaticJsonDocument : public JsonDocument {
-  static const size_t _capacity =
-      AddPadding<Max<1, desiredCapacity>::value>::value;
+  static const size_t capacity_ =
+      detail::AddPadding<detail::Max<1, desiredCapacity>::value>::value;
 
  public:
-  StaticJsonDocument() : JsonDocument(_buffer, _capacity) {}
+  StaticJsonDocument() : JsonDocument(buffer_, capacity_) {}
 
   StaticJsonDocument(const StaticJsonDocument& src)
-      : JsonDocument(_buffer, _capacity) {
+      : JsonDocument(buffer_, capacity_) {
     set(src);
   }
 
   template <typename T>
   StaticJsonDocument(
       const T& src,
-      typename enable_if<is_convertible<T, JsonVariantConst>::value>::type* = 0)
-      : JsonDocument(_buffer, _capacity) {
+      typename detail::enable_if<
+          detail::is_convertible<T, JsonVariantConst>::value>::type* = 0)
+      : JsonDocument(buffer_, capacity_) {
     set(src);
   }
 
   // disambiguate
-  StaticJsonDocument(JsonVariant src) : JsonDocument(_buffer, _capacity) {
+  StaticJsonDocument(JsonVariant src) : JsonDocument(buffer_, capacity_) {
     set(src);
   }
 
@@ -54,7 +55,7 @@ class StaticJsonDocument : public JsonDocument {
   }
 
  private:
-  char _buffer[_capacity];
+  char buffer_[capacity_];
 };
 
-}  // namespace ARDUINOJSON_NAMESPACE
+ARDUINOJSON_END_PUBLIC_NAMESPACE
diff --git a/neosensor/libraries/ArduinoJson/src/ArduinoJson/Json/EscapeSequence.hpp b/neosensor/libraries/ArduinoJson/src/ArduinoJson/Json/EscapeSequence.hpp
index 7d28a828..d2740cc4 100644
--- a/neosensor/libraries/ArduinoJson/src/ArduinoJson/Json/EscapeSequence.hpp
+++ b/neosensor/libraries/ArduinoJson/src/ArduinoJson/Json/EscapeSequence.hpp
@@ -1,12 +1,12 @@
 // ArduinoJson - https://arduinojson.org
-// Copyright © 2014-2022, Benoit BLANCHON
+// Copyright © 2014-2023, Benoit BLANCHON
 // MIT License
 
 #pragma once
 
 #include <ArduinoJson/Namespace.hpp>
 
-namespace ARDUINOJSON_NAMESPACE {
+ARDUINOJSON_BEGIN_PRIVATE_NAMESPACE
 
 class EscapeSequence {
  public:
@@ -36,4 +36,5 @@ class EscapeSequence {
     return &"//\"\"\\\\b\bf\fn\nr\rt\t"[excludeSolidus ? 2 : 0];
   }
 };
-}  // namespace ARDUINOJSON_NAMESPACE
+
+ARDUINOJSON_END_PRIVATE_NAMESPACE
diff --git a/neosensor/libraries/ArduinoJson/src/ArduinoJson/Json/JsonDeserializer.hpp b/neosensor/libraries/ArduinoJson/src/ArduinoJson/Json/JsonDeserializer.hpp
index 894e6d34..a566aea9 100644
--- a/neosensor/libraries/ArduinoJson/src/ArduinoJson/Json/JsonDeserializer.hpp
+++ b/neosensor/libraries/ArduinoJson/src/ArduinoJson/Json/JsonDeserializer.hpp
@@ -1,5 +1,5 @@
 // ArduinoJson - https://arduinojson.org
-// Copyright © 2014-2022, Benoit BLANCHON
+// Copyright © 2014-2023, Benoit BLANCHON
 // MIT License
 
 #pragma once
@@ -13,28 +13,29 @@
 #include <ArduinoJson/Numbers/parseNumber.hpp>
 #include <ArduinoJson/Polyfills/assert.hpp>
 #include <ArduinoJson/Polyfills/type_traits.hpp>
+#include <ArduinoJson/Polyfills/utility.hpp>
 #include <ArduinoJson/Variant/VariantData.hpp>
 
-namespace ARDUINOJSON_NAMESPACE {
+ARDUINOJSON_BEGIN_PRIVATE_NAMESPACE
 
 template <typename TReader, typename TStringStorage>
 class JsonDeserializer {
  public:
   JsonDeserializer(MemoryPool* pool, TReader reader,
                    TStringStorage stringStorage)
-      : _stringStorage(stringStorage),
-        _foundSomething(false),
-        _latch(reader),
-        _pool(pool) {}
+      : stringStorage_(stringStorage),
+        foundSomething_(false),
+        latch_(reader),
+        pool_(pool) {}
 
   template <typename TFilter>
   DeserializationError parse(VariantData& variant, TFilter filter,
-                             NestingLimit nestingLimit) {
+                             DeserializationOption::NestingLimit nestingLimit) {
     DeserializationError::Code err;
 
     err = parseVariant(variant, filter, nestingLimit);
 
-    if (!err && _latch.last() != 0 && !variant.isEnclosed()) {
+    if (!err && latch_.last() != 0 && !variant.isEnclosed()) {
       // We don't detect trailing characters earlier, so we need to check now
       return DeserializationError::InvalidInput;
     }
@@ -44,11 +45,11 @@ class JsonDeserializer {
 
  private:
   char current() {
-    return _latch.current();
+    return latch_.current();
   }
 
   void move() {
-    _latch.clear();
+    latch_.clear();
   }
 
   bool eat(char charToSkip) {
@@ -59,8 +60,9 @@ class JsonDeserializer {
   }
 
   template <typename TFilter>
-  DeserializationError::Code parseVariant(VariantData& variant, TFilter filter,
-                                          NestingLimit nestingLimit) {
+  DeserializationError::Code parseVariant(
+      VariantData& variant, TFilter filter,
+      DeserializationOption::NestingLimit nestingLimit) {
     DeserializationError::Code err;
 
     err = skipSpacesAndComments();
@@ -110,7 +112,8 @@ class JsonDeserializer {
     }
   }
 
-  DeserializationError::Code skipVariant(NestingLimit nestingLimit) {
+  DeserializationError::Code skipVariant(
+      DeserializationOption::NestingLimit nestingLimit) {
     DeserializationError::Code err;
 
     err = skipSpacesAndComments();
@@ -143,8 +146,9 @@ class JsonDeserializer {
   }
 
   template <typename TFilter>
-  DeserializationError::Code parseArray(CollectionData& array, TFilter filter,
-                                        NestingLimit nestingLimit) {
+  DeserializationError::Code parseArray(
+      CollectionData& array, TFilter filter,
+      DeserializationOption::NestingLimit nestingLimit) {
     DeserializationError::Code err;
 
     if (nestingLimit.reached())
@@ -169,7 +173,7 @@ class JsonDeserializer {
     for (;;) {
       if (memberFilter.allow()) {
         // Allocate slot in array
-        VariantData* value = array.addElement(_pool);
+        VariantData* value = array.addElement(pool_);
         if (!value)
           return DeserializationError::NoMemory;
 
@@ -196,7 +200,8 @@ class JsonDeserializer {
     }
   }
 
-  DeserializationError::Code skipArray(NestingLimit nestingLimit) {
+  DeserializationError::Code skipArray(
+      DeserializationOption::NestingLimit nestingLimit) {
     DeserializationError::Code err;
 
     if (nestingLimit.reached())
@@ -227,8 +232,9 @@ class JsonDeserializer {
   }
 
   template <typename TFilter>
-  DeserializationError::Code parseObject(CollectionData& object, TFilter filter,
-                                         NestingLimit nestingLimit) {
+  DeserializationError::Code parseObject(
+      CollectionData& object, TFilter filter,
+      DeserializationOption::NestingLimit nestingLimit) {
     DeserializationError::Code err;
 
     if (nestingLimit.reached())
@@ -263,7 +269,7 @@ class JsonDeserializer {
       if (!eat(':'))
         return DeserializationError::InvalidInput;
 
-      JsonString key = _stringStorage.str();
+      JsonString key = stringStorage_.str();
 
       TFilter memberFilter = filter[key.c_str()];
 
@@ -272,10 +278,10 @@ class JsonDeserializer {
         if (!variant) {
           // Save key in memory pool.
           // This MUST be done before adding the slot.
-          key = _stringStorage.save();
+          key = stringStorage_.save();
 
           // Allocate slot in object
-          VariantSlot* slot = object.addSlot(_pool);
+          VariantSlot* slot = object.addSlot(pool_);
           if (!slot)
             return DeserializationError::NoMemory;
 
@@ -312,7 +318,8 @@ class JsonDeserializer {
     }
   }
 
-  DeserializationError::Code skipObject(NestingLimit nestingLimit) {
+  DeserializationError::Code skipObject(
+      DeserializationOption::NestingLimit nestingLimit) {
     DeserializationError::Code err;
 
     if (nestingLimit.reached())
@@ -370,7 +377,7 @@ class JsonDeserializer {
   }
 
   DeserializationError::Code parseKey() {
-    _stringStorage.startString();
+    stringStorage_.startString();
     if (isQuote(current())) {
       return parseQuotedString();
     } else {
@@ -381,13 +388,13 @@ class JsonDeserializer {
   DeserializationError::Code parseStringValue(VariantData& variant) {
     DeserializationError::Code err;
 
-    _stringStorage.startString();
+    stringStorage_.startString();
 
     err = parseQuotedString();
     if (err)
       return err;
 
-    variant.setString(_stringStorage.save());
+    variant.setString(stringStorage_.save());
 
     return DeserializationError::Ok;
   }
@@ -423,9 +430,9 @@ class JsonDeserializer {
           if (err)
             return err;
           if (codepoint.append(codeunit))
-            Utf8::encodeCodepoint(codepoint.value(), _stringStorage);
+            Utf8::encodeCodepoint(codepoint.value(), stringStorage_);
 #else
-          _stringStorage.append('\\');
+          stringStorage_.append('\\');
 #endif
           continue;
         }
@@ -437,10 +444,10 @@ class JsonDeserializer {
         move();
       }
 
-      _stringStorage.append(c);
+      stringStorage_.append(c);
     }
 
-    if (!_stringStorage.isValid())
+    if (!stringStorage_.isValid())
       return DeserializationError::NoMemory;
 
     return DeserializationError::Ok;
@@ -453,14 +460,14 @@ class JsonDeserializer {
     if (canBeInNonQuotedString(c)) {  // no quotes
       do {
         move();
-        _stringStorage.append(c);
+        stringStorage_.append(c);
         c = current();
       } while (canBeInNonQuotedString(c));
     } else {
       return DeserializationError::InvalidInput;
     }
 
-    if (!_stringStorage.isValid())
+    if (!stringStorage_.isValid())
       return DeserializationError::NoMemory;
 
     return DeserializationError::Ok;
@@ -509,12 +516,12 @@ class JsonDeserializer {
     char c = current();
     while (canBeInNumber(c) && n < 63) {
       move();
-      _buffer[n++] = c;
+      buffer_[n++] = c;
       c = current();
     }
-    _buffer[n] = 0;
+    buffer_[n] = 0;
 
-    if (!parseNumber(_buffer, result))
+    if (!parseNumber(buffer_, result))
       return DeserializationError::InvalidInput;
 
     return DeserializationError::Ok;
@@ -578,7 +585,7 @@ class JsonDeserializer {
       switch (current()) {
         // end of string
         case '\0':
-          return _foundSomething ? DeserializationError::IncompleteInput
+          return foundSomething_ ? DeserializationError::IncompleteInput
                                  : DeserializationError::EmptyInput;
 
         // spaces
@@ -633,7 +640,7 @@ class JsonDeserializer {
 #endif
 
         default:
-          _foundSomething = true;
+          foundSomething_ = true;
           return DeserializationError::Ok;
       }
     }
@@ -652,124 +659,35 @@ class JsonDeserializer {
     return DeserializationError::Ok;
   }
 
-  TStringStorage _stringStorage;
-  bool _foundSomething;
-  Latch<TReader> _latch;
-  MemoryPool* _pool;
-  char _buffer[64];  // using a member instead of a local variable because it
+  TStringStorage stringStorage_;
+  bool foundSomething_;
+  Latch<TReader> latch_;
+  MemoryPool* pool_;
+  char buffer_[64];  // using a member instead of a local variable because it
                      // ended in the recursive path after compiler inlined the
                      // code
 };
 
-// Parses a JSON input and puts the result in a JsonDocument.
-// https://arduinojson.org/v6/api/json/deserializejson/
-template <typename TString>
-DeserializationError deserializeJson(
-    JsonDocument& doc, const TString& input,
-    NestingLimit nestingLimit = NestingLimit()) {
-  return deserialize<JsonDeserializer>(doc, input, nestingLimit,
-                                       AllowAllFilter());
-}
-
-// Parses a JSON input, filters, and puts the result in a JsonDocument.
-// https://arduinojson.org/v6/api/json/deserializejson/
-template <typename TString>
-DeserializationError deserializeJson(
-    JsonDocument& doc, const TString& input, Filter filter,
-    NestingLimit nestingLimit = NestingLimit()) {
-  return deserialize<JsonDeserializer>(doc, input, nestingLimit, filter);
-}
-
-// Parses a JSON input, filters, and puts the result in a JsonDocument.
-// https://arduinojson.org/v6/api/json/deserializejson/
-template <typename TString>
-DeserializationError deserializeJson(JsonDocument& doc, const TString& input,
-                                     NestingLimit nestingLimit, Filter filter) {
-  return deserialize<JsonDeserializer>(doc, input, nestingLimit, filter);
-}
+ARDUINOJSON_END_PRIVATE_NAMESPACE
 
-// Parses a JSON input and puts the result in a JsonDocument.
-// https://arduinojson.org/v6/api/json/deserializejson/
-template <typename TStream>
-DeserializationError deserializeJson(
-    JsonDocument& doc, TStream& input,
-    NestingLimit nestingLimit = NestingLimit()) {
-  return deserialize<JsonDeserializer>(doc, input, nestingLimit,
-                                       AllowAllFilter());
-}
-
-// Parses a JSON input, filters, and puts the result in a JsonDocument.
-// https://arduinojson.org/v6/api/json/deserializejson/
-template <typename TStream>
-DeserializationError deserializeJson(
-    JsonDocument& doc, TStream& input, Filter filter,
-    NestingLimit nestingLimit = NestingLimit()) {
-  return deserialize<JsonDeserializer>(doc, input, nestingLimit, filter);
-}
-
-// Parses a JSON input, filters, and puts the result in a JsonDocument.
-// https://arduinojson.org/v6/api/json/deserializejson/
-template <typename TStream>
-DeserializationError deserializeJson(JsonDocument& doc, TStream& input,
-                                     NestingLimit nestingLimit, Filter filter) {
-  return deserialize<JsonDeserializer>(doc, input, nestingLimit, filter);
-}
-
-// Parses a JSON input and puts the result in a JsonDocument.
-// https://arduinojson.org/v6/api/json/deserializejson/
-template <typename TChar>
-DeserializationError deserializeJson(
-    JsonDocument& doc, TChar* input,
-    NestingLimit nestingLimit = NestingLimit()) {
-  return deserialize<JsonDeserializer>(doc, input, nestingLimit,
-                                       AllowAllFilter());
-}
-
-// Parses a JSON input, filters, and puts the result in a JsonDocument.
-// https://arduinojson.org/v6/api/json/deserializejson/
-template <typename TChar>
-DeserializationError deserializeJson(
-    JsonDocument& doc, TChar* input, Filter filter,
-    NestingLimit nestingLimit = NestingLimit()) {
-  return deserialize<JsonDeserializer>(doc, input, nestingLimit, filter);
-}
-
-// Parses a JSON input, filters, and puts the result in a JsonDocument.
-// https://arduinojson.org/v6/api/json/deserializejson/
-template <typename TChar>
-DeserializationError deserializeJson(JsonDocument& doc, TChar* input,
-                                     NestingLimit nestingLimit, Filter filter) {
-  return deserialize<JsonDeserializer>(doc, input, nestingLimit, filter);
-}
-
-// Parses a JSON input and puts the result in a JsonDocument.
-// https://arduinojson.org/v6/api/json/deserializejson/
-template <typename TChar>
-DeserializationError deserializeJson(
-    JsonDocument& doc, TChar* input, size_t inputSize,
-    NestingLimit nestingLimit = NestingLimit()) {
-  return deserialize<JsonDeserializer>(doc, input, inputSize, nestingLimit,
-                                       AllowAllFilter());
-}
+ARDUINOJSON_BEGIN_PUBLIC_NAMESPACE
 
 // Parses a JSON input, filters, and puts the result in a JsonDocument.
 // https://arduinojson.org/v6/api/json/deserializejson/
-template <typename TChar>
-DeserializationError deserializeJson(
-    JsonDocument& doc, TChar* input, size_t inputSize, Filter filter,
-    NestingLimit nestingLimit = NestingLimit()) {
-  return deserialize<JsonDeserializer>(doc, input, inputSize, nestingLimit,
-                                       filter);
+template <typename... Args>
+DeserializationError deserializeJson(JsonDocument& doc, Args&&... args) {
+  using namespace detail;
+  return deserialize<JsonDeserializer>(doc, detail::forward<Args>(args)...);
 }
 
 // Parses a JSON input, filters, and puts the result in a JsonDocument.
 // https://arduinojson.org/v6/api/json/deserializejson/
-template <typename TChar>
+template <typename TChar, typename... Args>
 DeserializationError deserializeJson(JsonDocument& doc, TChar* input,
-                                     size_t inputSize,
-                                     NestingLimit nestingLimit, Filter filter) {
-  return deserialize<JsonDeserializer>(doc, input, inputSize, nestingLimit,
-                                       filter);
+                                     Args&&... args) {
+  using namespace detail;
+  return deserialize<JsonDeserializer>(doc, input,
+                                       detail::forward<Args>(args)...);
 }
 
-}  // namespace ARDUINOJSON_NAMESPACE
+ARDUINOJSON_END_PUBLIC_NAMESPACE
diff --git a/neosensor/libraries/ArduinoJson/src/ArduinoJson/Json/JsonSerializer.hpp b/neosensor/libraries/ArduinoJson/src/ArduinoJson/Json/JsonSerializer.hpp
index 563f12e8..781c9c93 100644
--- a/neosensor/libraries/ArduinoJson/src/ArduinoJson/Json/JsonSerializer.hpp
+++ b/neosensor/libraries/ArduinoJson/src/ArduinoJson/Json/JsonSerializer.hpp
@@ -1,5 +1,5 @@
 // ArduinoJson - https://arduinojson.org
-// Copyright © 2014-2022, Benoit BLANCHON
+// Copyright © 2014-2023, Benoit BLANCHON
 // MIT License
 
 #pragma once
@@ -9,14 +9,14 @@
 #include <ArduinoJson/Serialization/serialize.hpp>
 #include <ArduinoJson/Variant/Visitor.hpp>
 
-namespace ARDUINOJSON_NAMESPACE {
+ARDUINOJSON_BEGIN_PRIVATE_NAMESPACE
 
 template <typename TWriter>
 class JsonSerializer : public Visitor<size_t> {
  public:
   static const bool producesText = true;
 
-  JsonSerializer(TWriter writer) : _formatter(writer) {}
+  JsonSerializer(TWriter writer) : formatter_(writer) {}
 
   FORCE_INLINE size_t visitArray(const CollectionData& array) {
     write('[');
@@ -43,7 +43,7 @@ class JsonSerializer : public Visitor<size_t> {
     const VariantSlot* slot = object.head();
 
     while (slot != 0) {
-      _formatter.writeString(slot->key());
+      formatter_.writeString(slot->key());
       write(':');
       slot->data()->accept(*this);
 
@@ -59,66 +59,71 @@ class JsonSerializer : public Visitor<size_t> {
   }
 
   size_t visitFloat(JsonFloat value) {
-    _formatter.writeFloat(value);
+    formatter_.writeFloat(value);
     return bytesWritten();
   }
 
   size_t visitString(const char* value) {
-    _formatter.writeString(value);
+    formatter_.writeString(value);
     return bytesWritten();
   }
 
   size_t visitString(const char* value, size_t n) {
-    _formatter.writeString(value, n);
+    formatter_.writeString(value, n);
     return bytesWritten();
   }
 
   size_t visitRawJson(const char* data, size_t n) {
-    _formatter.writeRaw(data, n);
+    formatter_.writeRaw(data, n);
     return bytesWritten();
   }
 
   size_t visitSignedInteger(JsonInteger value) {
-    _formatter.writeInteger(value);
+    formatter_.writeInteger(value);
     return bytesWritten();
   }
 
   size_t visitUnsignedInteger(JsonUInt value) {
-    _formatter.writeInteger(value);
+    formatter_.writeInteger(value);
     return bytesWritten();
   }
 
   size_t visitBoolean(bool value) {
-    _formatter.writeBoolean(value);
+    formatter_.writeBoolean(value);
     return bytesWritten();
   }
 
   size_t visitNull() {
-    _formatter.writeRaw("null");
+    formatter_.writeRaw("null");
     return bytesWritten();
   }
 
  protected:
   size_t bytesWritten() const {
-    return _formatter.bytesWritten();
+    return formatter_.bytesWritten();
   }
 
   void write(char c) {
-    _formatter.writeRaw(c);
+    formatter_.writeRaw(c);
   }
 
   void write(const char* s) {
-    _formatter.writeRaw(s);
+    formatter_.writeRaw(s);
   }
 
  private:
-  TextFormatter<TWriter> _formatter;
+  TextFormatter<TWriter> formatter_;
 };
 
+ARDUINOJSON_END_PRIVATE_NAMESPACE
+
+ARDUINOJSON_BEGIN_PUBLIC_NAMESPACE
+
 // Produces a minified JSON document.
 // https://arduinojson.org/v6/api/json/serializejson/
 template <typename TDestination>
 size_t serializeJson(JsonVariantConst source, TDestination& destination) {
+  using namespace detail;
   return serialize<JsonSerializer>(source, destination);
 }
 
@@ -126,23 +131,25 @@ size_t serializeJson(JsonVariantConst source, TDestination& destination) {
 // https://arduinojson.org/v6/api/json/serializejson/
 inline size_t serializeJson(JsonVariantConst source, void* buffer,
                             size_t bufferSize) {
+  using namespace detail;
   return serialize<JsonSerializer>(source, buffer, bufferSize);
 }
 
 // Computes the length of the document that serializeJson() produces.
 // https://arduinojson.org/v6/api/json/measurejson/
 inline size_t measureJson(JsonVariantConst source) {
+  using namespace detail;
   return measure<JsonSerializer>(source);
 }
 
 #if ARDUINOJSON_ENABLE_STD_STREAM
 template <typename T>
-inline typename enable_if<is_convertible<T, JsonVariantConst>::value,
-                          std::ostream&>::type
+inline typename detail::enable_if<
+    detail::is_convertible<T, JsonVariantConst>::value, std::ostream&>::type
 operator<<(std::ostream& os, const T& source) {
   serializeJson(source, os);
   return os;
 }
 #endif
 
-}  // namespace ARDUINOJSON_NAMESPACE
+ARDUINOJSON_END_PUBLIC_NAMESPACE
diff --git a/neosensor/libraries/ArduinoJson/src/ArduinoJson/Json/Latch.hpp b/neosensor/libraries/ArduinoJson/src/ArduinoJson/Json/Latch.hpp
index c422fef1..08070eb3 100644
--- a/neosensor/libraries/ArduinoJson/src/ArduinoJson/Json/Latch.hpp
+++ b/neosensor/libraries/ArduinoJson/src/ArduinoJson/Json/Latch.hpp
@@ -1,56 +1,56 @@
 // ArduinoJson - https://arduinojson.org
-// Copyright © 2014-2022, Benoit BLANCHON
+// Copyright © 2014-2023, Benoit BLANCHON
 // MIT License
 
 #pragma once
 
 #include <ArduinoJson/Polyfills/assert.hpp>
 
-namespace ARDUINOJSON_NAMESPACE {
+ARDUINOJSON_BEGIN_PRIVATE_NAMESPACE
 
 template <typename TReader>
 class Latch {
  public:
-  Latch(TReader reader) : _reader(reader), _loaded(false) {
+  Latch(TReader reader) : reader_(reader), loaded_(false) {
 #if ARDUINOJSON_DEBUG
-    _ended = false;
+    ended_ = false;
 #endif
   }
 
   void clear() {
-    _loaded = false;
+    loaded_ = false;
   }
 
   int last() const {
-    return _current;
+    return current_;
   }
 
   FORCE_INLINE char current() {
-    if (!_loaded) {
+    if (!loaded_) {
       load();
     }
-    return _current;
+    return current_;
   }
 
  private:
   void load() {
-    ARDUINOJSON_ASSERT(!_ended);
-    int c = _reader.read();
+    ARDUINOJSON_ASSERT(!ended_);
+    int c = reader_.read();
 #if ARDUINOJSON_DEBUG
     if (c <= 0)
-      _ended = true;
+      ended_ = true;
 #endif
-    _current = static_cast<char>(c > 0 ? c : 0);
-    _loaded = true;
+    current_ = static_cast<char>(c > 0 ? c : 0);
+    loaded_ = true;
   }
 
-  TReader _reader;
-  char _current;  // NOLINT(clang-analyzer-optin.cplusplus.UninitializedObject)
+  TReader reader_;
+  char current_;  // NOLINT(clang-analyzer-optin.cplusplus.UninitializedObject)
                   // Not initialized in constructor (+10 bytes on AVR)
-  bool _loaded;
+  bool loaded_;
 #if ARDUINOJSON_DEBUG
-  bool _ended;
+  bool ended_;
 #endif
 };
 
-}  // namespace ARDUINOJSON_NAMESPACE
+ARDUINOJSON_END_PRIVATE_NAMESPACE
diff --git a/neosensor/libraries/ArduinoJson/src/ArduinoJson/Json/PrettyJsonSerializer.hpp b/neosensor/libraries/ArduinoJson/src/ArduinoJson/Json/PrettyJsonSerializer.hpp
index e14ab0db..e8a92064 100644
--- a/neosensor/libraries/ArduinoJson/src/ArduinoJson/Json/PrettyJsonSerializer.hpp
+++ b/neosensor/libraries/ArduinoJson/src/ArduinoJson/Json/PrettyJsonSerializer.hpp
@@ -1,5 +1,5 @@
 // ArduinoJson - https://arduinojson.org
-// Copyright © 2014-2022, Benoit BLANCHON
+// Copyright © 2014-2023, Benoit BLANCHON
 // MIT License
 
 #pragma once
@@ -9,20 +9,20 @@
 #include <ArduinoJson/Serialization/measure.hpp>
 #include <ArduinoJson/Serialization/serialize.hpp>
 
-namespace ARDUINOJSON_NAMESPACE {
+ARDUINOJSON_BEGIN_PRIVATE_NAMESPACE
 
 template <typename TWriter>
 class PrettyJsonSerializer : public JsonSerializer<TWriter> {
   typedef JsonSerializer<TWriter> base;
 
  public:
-  PrettyJsonSerializer(TWriter writer) : base(writer), _nesting(0) {}
+  PrettyJsonSerializer(TWriter writer) : base(writer), nesting_(0) {}
 
   size_t visitArray(const CollectionData& array) {
     const VariantSlot* slot = array.head();
     if (slot) {
       base::write("[\r\n");
-      _nesting++;
+      nesting_++;
       while (slot != 0) {
         indent();
         slot->data()->accept(*this);
@@ -30,7 +30,7 @@ class PrettyJsonSerializer : public JsonSerializer<TWriter> {
         slot = slot->next();
         base::write(slot ? ",\r\n" : "\r\n");
       }
-      _nesting--;
+      nesting_--;
       indent();
       base::write("]");
     } else {
@@ -43,7 +43,7 @@ class PrettyJsonSerializer : public JsonSerializer<TWriter> {
     const VariantSlot* slot = object.head();
     if (slot) {
       base::write("{\r\n");
-      _nesting++;
+      nesting_++;
       while (slot != 0) {
         indent();
         base::visitString(slot->key());
@@ -53,7 +53,7 @@ class PrettyJsonSerializer : public JsonSerializer<TWriter> {
         slot = slot->next();
         base::write(slot ? ",\r\n" : "\r\n");
       }
-      _nesting--;
+      nesting_--;
       indent();
       base::write("}");
     } else {
@@ -64,17 +64,22 @@ class PrettyJsonSerializer : public JsonSerializer<TWriter> {
 
  private:
   void indent() {
-    for (uint8_t i = 0; i < _nesting; i++)
+    for (uint8_t i = 0; i < nesting_; i++)
       base::write(ARDUINOJSON_TAB);
   }
 
-  uint8_t _nesting;
+  uint8_t nesting_;
 };
 
+ARDUINOJSON_END_PRIVATE_NAMESPACE
+
+ARDUINOJSON_BEGIN_PUBLIC_NAMESPACE
+
 // Produces JsonDocument to create a prettified JSON document.
 // https://arduinojson.org/v6/api/json/serializejsonpretty/
 template <typename TDestination>
 size_t serializeJsonPretty(JsonVariantConst source, TDestination& destination) {
+  using namespace ArduinoJson::detail;
   return serialize<PrettyJsonSerializer>(source, destination);
 }
 
@@ -82,13 +87,15 @@ size_t serializeJsonPretty(JsonVariantConst source, TDestination& destination) {
 // https://arduinojson.org/v6/api/json/serializejsonpretty/
 inline size_t serializeJsonPretty(JsonVariantConst source, void* buffer,
                                   size_t bufferSize) {
+  using namespace ArduinoJson::detail;
   return serialize<PrettyJsonSerializer>(source, buffer, bufferSize);
 }
 
 // Computes the length of the document that serializeJsonPretty() produces.
 // https://arduinojson.org/v6/api/json/measurejsonpretty/
 inline size_t measureJsonPretty(JsonVariantConst source) {
+  using namespace ArduinoJson::detail;
   return measure<PrettyJsonSerializer>(source);
 }
 
-}  // namespace ARDUINOJSON_NAMESPACE
+ARDUINOJSON_END_PUBLIC_NAMESPACE
diff --git a/neosensor/libraries/ArduinoJson/src/ArduinoJson/Json/TextFormatter.hpp b/neosensor/libraries/ArduinoJson/src/ArduinoJson/Json/TextFormatter.hpp
index 2080b14c..f8eb2822 100644
--- a/neosensor/libraries/ArduinoJson/src/ArduinoJson/Json/TextFormatter.hpp
+++ b/neosensor/libraries/ArduinoJson/src/ArduinoJson/Json/TextFormatter.hpp
@@ -1,5 +1,5 @@
 // ArduinoJson - https://arduinojson.org
-// Copyright © 2014-2022, Benoit BLANCHON
+// Copyright © 2014-2023, Benoit BLANCHON
 // MIT License
 
 #pragma once
@@ -15,16 +15,18 @@
 #include <ArduinoJson/Polyfills/type_traits.hpp>
 #include <ArduinoJson/Serialization/CountingDecorator.hpp>
 
-namespace ARDUINOJSON_NAMESPACE {
+ARDUINOJSON_BEGIN_PRIVATE_NAMESPACE
 
 template <typename TWriter>
 class TextFormatter {
  public:
-  explicit TextFormatter(TWriter writer) : _writer(writer) {}
+  explicit TextFormatter(TWriter writer) : writer_(writer) {}
+
+  TextFormatter& operator=(const TextFormatter&) = delete;
 
   // Returns the number of bytes sent to the TWriter implementation.
   size_t bytesWritten() const {
-    return _writer.count();
+    return writer_.count();
   }
 
   void writeBoolean(bool value) {
@@ -144,30 +146,28 @@ class TextFormatter {
   }
 
   void writeRaw(const char* s) {
-    _writer.write(reinterpret_cast<const uint8_t*>(s), strlen(s));
+    writer_.write(reinterpret_cast<const uint8_t*>(s), strlen(s));
   }
 
   void writeRaw(const char* s, size_t n) {
-    _writer.write(reinterpret_cast<const uint8_t*>(s), n);
+    writer_.write(reinterpret_cast<const uint8_t*>(s), n);
   }
 
   void writeRaw(const char* begin, const char* end) {
-    _writer.write(reinterpret_cast<const uint8_t*>(begin),
+    writer_.write(reinterpret_cast<const uint8_t*>(begin),
                   static_cast<size_t>(end - begin));
   }
 
   template <size_t N>
   void writeRaw(const char (&s)[N]) {
-    _writer.write(reinterpret_cast<const uint8_t*>(s), N - 1);
+    writer_.write(reinterpret_cast<const uint8_t*>(s), N - 1);
   }
   void writeRaw(char c) {
-    _writer.write(static_cast<uint8_t>(c));
+    writer_.write(static_cast<uint8_t>(c));
   }
 
  protected:
-  CountingDecorator<TWriter> _writer;
-
- private:
-  TextFormatter& operator=(const TextFormatter&);  // cannot be assigned
+  CountingDecorator<TWriter> writer_;
 };
-}  // namespace ARDUINOJSON_NAMESPACE
+
+ARDUINOJSON_END_PRIVATE_NAMESPACE
diff --git a/neosensor/libraries/ArduinoJson/src/ArduinoJson/Json/Utf16.hpp b/neosensor/libraries/ArduinoJson/src/ArduinoJson/Json/Utf16.hpp
index 3a22bf3a..6e314db2 100644
--- a/neosensor/libraries/ArduinoJson/src/ArduinoJson/Json/Utf16.hpp
+++ b/neosensor/libraries/ArduinoJson/src/ArduinoJson/Json/Utf16.hpp
@@ -1,5 +1,5 @@
 // ArduinoJson - https://arduinojson.org
-// Copyright © 2014-2022, Benoit BLANCHON
+// Copyright © 2014-2023, Benoit BLANCHON
 // MIT License
 
 #pragma once
@@ -18,7 +18,7 @@
 #  endif
 #endif
 
-namespace ARDUINOJSON_NAMESPACE {
+ARDUINOJSON_BEGIN_PRIVATE_NAMESPACE
 
 namespace Utf16 {
 inline bool isHighSurrogate(uint16_t codeunit) {
@@ -31,34 +31,34 @@ inline bool isLowSurrogate(uint16_t codeunit) {
 
 class Codepoint {
  public:
-  Codepoint() : _highSurrogate(0), _codepoint(0) {}
+  Codepoint() : highSurrogate_(0), codepoint_(0) {}
 
   bool append(uint16_t codeunit) {
     if (isHighSurrogate(codeunit)) {
-      _highSurrogate = codeunit & 0x3FF;
+      highSurrogate_ = codeunit & 0x3FF;
       return false;
     }
 
     if (isLowSurrogate(codeunit)) {
-      _codepoint =
-          uint32_t(0x10000 + ((_highSurrogate << 10) | (codeunit & 0x3FF)));
+      codepoint_ =
+          uint32_t(0x10000 + ((highSurrogate_ << 10) | (codeunit & 0x3FF)));
       return true;
     }
 
-    _codepoint = codeunit;
+    codepoint_ = codeunit;
     return true;
   }
 
   uint32_t value() const {
-    return _codepoint;
+    return codepoint_;
   }
 
  private:
-  uint16_t _highSurrogate;
-  uint32_t _codepoint;
+  uint16_t highSurrogate_;
+  uint32_t codepoint_;
 };
 }  // namespace Utf16
-}  // namespace ARDUINOJSON_NAMESPACE
+ARDUINOJSON_END_PRIVATE_NAMESPACE
 
 #if defined(__GNUC__)
 #  if __GNUC__ >= 8
diff --git a/neosensor/libraries/ArduinoJson/src/ArduinoJson/Json/Utf8.hpp b/neosensor/libraries/ArduinoJson/src/ArduinoJson/Json/Utf8.hpp
index 0ec23ac7..1e33c186 100644
--- a/neosensor/libraries/ArduinoJson/src/ArduinoJson/Json/Utf8.hpp
+++ b/neosensor/libraries/ArduinoJson/src/ArduinoJson/Json/Utf8.hpp
@@ -1,12 +1,12 @@
 // ArduinoJson - https://arduinojson.org
-// Copyright © 2014-2022, Benoit BLANCHON
+// Copyright © 2014-2023, Benoit BLANCHON
 // MIT License
 
 #pragma once
 
 #include <ArduinoJson/Namespace.hpp>
 
-namespace ARDUINOJSON_NAMESPACE {
+ARDUINOJSON_BEGIN_PRIVATE_NAMESPACE
 
 namespace Utf8 {
 template <typename TStringBuilder>
@@ -43,4 +43,4 @@ inline void encodeCodepoint(uint32_t codepoint32, TStringBuilder& str) {
   }
 }
 }  // namespace Utf8
-}  // namespace ARDUINOJSON_NAMESPACE
+ARDUINOJSON_END_PRIVATE_NAMESPACE
diff --git a/neosensor/libraries/ArduinoJson/src/ArduinoJson/Memory/Alignment.hpp b/neosensor/libraries/ArduinoJson/src/ArduinoJson/Memory/Alignment.hpp
index e9a6baff..61b8654e 100644
--- a/neosensor/libraries/ArduinoJson/src/ArduinoJson/Memory/Alignment.hpp
+++ b/neosensor/libraries/ArduinoJson/src/ArduinoJson/Memory/Alignment.hpp
@@ -1,5 +1,5 @@
 // ArduinoJson - https://arduinojson.org
-// Copyright © 2014-2022, Benoit BLANCHON
+// Copyright © 2014-2023, Benoit BLANCHON
 // MIT License
 
 #pragma once
@@ -8,7 +8,7 @@
 
 #include <stddef.h>  // size_t
 
-namespace ARDUINOJSON_NAMESPACE {
+ARDUINOJSON_BEGIN_PRIVATE_NAMESPACE
 
 #if ARDUINOJSON_ENABLE_ALIGNMENT
 
@@ -57,4 +57,4 @@ inline T* addPadding(T* p) {
   return reinterpret_cast<T*>(address);
 }
 
-}  // namespace ARDUINOJSON_NAMESPACE
+ARDUINOJSON_END_PRIVATE_NAMESPACE
diff --git a/neosensor/libraries/ArduinoJson/src/ArduinoJson/Memory/MemoryPool.hpp b/neosensor/libraries/ArduinoJson/src/ArduinoJson/Memory/MemoryPool.hpp
index a0329deb..754d5851 100644
--- a/neosensor/libraries/ArduinoJson/src/ArduinoJson/Memory/MemoryPool.hpp
+++ b/neosensor/libraries/ArduinoJson/src/ArduinoJson/Memory/MemoryPool.hpp
@@ -1,5 +1,5 @@
 // ArduinoJson - https://arduinojson.org
-// Copyright © 2014-2022, Benoit BLANCHON
+// Copyright © 2014-2023, Benoit BLANCHON
 // MIT License
 
 #pragma once
@@ -17,52 +17,52 @@
 // Computes the size required to store an array in a JsonDocument.
 // https://arduinojson.org/v6/how-to/determine-the-capacity-of-the-jsondocument/
 #define JSON_ARRAY_SIZE(NUMBER_OF_ELEMENTS) \
-  ((NUMBER_OF_ELEMENTS) * sizeof(ARDUINOJSON_NAMESPACE::VariantSlot))
+  ((NUMBER_OF_ELEMENTS) * sizeof(ArduinoJson::detail::VariantSlot))
 
 // Returns the size (in bytes) of an object with n elements.
 // Can be very handy to determine the size of a StaticMemoryPool.
 #define JSON_OBJECT_SIZE(NUMBER_OF_ELEMENTS) \
-  ((NUMBER_OF_ELEMENTS) * sizeof(ARDUINOJSON_NAMESPACE::VariantSlot))
+  ((NUMBER_OF_ELEMENTS) * sizeof(ArduinoJson::detail::VariantSlot))
 
-namespace ARDUINOJSON_NAMESPACE {
+ARDUINOJSON_BEGIN_PRIVATE_NAMESPACE
 
-// _begin                                   _end
+// begin_                                   end_
 // v                                           v
 // +-------------+--------------+--------------+
 // | strings...  |   (free)     |  ...variants |
 // +-------------+--------------+--------------+
 //               ^              ^
-//             _left          _right
+//             left_          right_
 
 class MemoryPool {
  public:
   MemoryPool(char* buf, size_t capa)
-      : _begin(buf),
-        _left(buf),
-        _right(buf ? buf + capa : 0),
-        _end(buf ? buf + capa : 0),
-        _overflowed(false) {
-    ARDUINOJSON_ASSERT(isAligned(_begin));
-    ARDUINOJSON_ASSERT(isAligned(_right));
-    ARDUINOJSON_ASSERT(isAligned(_end));
+      : begin_(buf),
+        left_(buf),
+        right_(buf ? buf + capa : 0),
+        end_(buf ? buf + capa : 0),
+        overflowed_(false) {
+    ARDUINOJSON_ASSERT(isAligned(begin_));
+    ARDUINOJSON_ASSERT(isAligned(right_));
+    ARDUINOJSON_ASSERT(isAligned(end_));
   }
 
   void* buffer() {
-    return _begin;  // NOLINT(clang-analyzer-unix.Malloc)
+    return begin_;  // NOLINT(clang-analyzer-unix.Malloc)
                     // movePointers() alters this pointer
   }
 
   // Gets the capacity of the memoryPool in bytes
   size_t capacity() const {
-    return size_t(_end - _begin);
+    return size_t(end_ - begin_);
   }
 
   size_t size() const {
-    return size_t(_left - _begin + _end - _right);
+    return size_t(left_ - begin_ + end_ - right_);
   }
 
   bool overflowed() const {
-    return _overflowed;
+    return overflowed_;
   }
 
   VariantSlot* allocVariant() {
@@ -91,40 +91,40 @@ class MemoryPool {
   }
 
   void getFreeZone(char** zoneStart, size_t* zoneSize) const {
-    *zoneStart = _left;
-    *zoneSize = size_t(_right - _left);
+    *zoneStart = left_;
+    *zoneSize = size_t(right_ - left_);
   }
 
   const char* saveStringFromFreeZone(size_t len) {
 #if ARDUINOJSON_ENABLE_STRING_DEDUPLICATION
-    const char* dup = findString(adaptString(_left, len));
+    const char* dup = findString(adaptString(left_, len));
     if (dup)
       return dup;
 #endif
 
-    const char* str = _left;
-    _left += len;
-    *_left++ = 0;
+    const char* str = left_;
+    left_ += len;
+    *left_++ = 0;
     checkInvariants();
     return str;
   }
 
   void markAsOverflowed() {
-    _overflowed = true;
+    overflowed_ = true;
   }
 
   void clear() {
-    _left = _begin;
-    _right = _end;
-    _overflowed = false;
+    left_ = begin_;
+    right_ = end_;
+    overflowed_ = false;
   }
 
   bool canAlloc(size_t bytes) const {
-    return _left + bytes <= _right;
+    return left_ + bytes <= right_;
   }
 
   bool owns(void* p) const {
-    return _begin <= p && p < _end;
+    return begin_ <= p && p < end_;
   }
 
   // Workaround for missing placement new
@@ -134,51 +134,51 @@ class MemoryPool {
 
   // Squash the free space between strings and variants
   //
-  // _begin                    _end
+  // begin_                    end_
   // v                            v
   // +-------------+--------------+
   // | strings...  |  ...variants |
   // +-------------+--------------+
   //               ^
-  //          _left _right
+  //          left_ right_
   //
   // This funcion is called before a realloc.
   ptrdiff_t squash() {
-    char* new_right = addPadding(_left);
-    if (new_right >= _right)
+    char* new_right = addPadding(left_);
+    if (new_right >= right_)
       return 0;
 
-    size_t right_size = static_cast<size_t>(_end - _right);
-    memmove(new_right, _right, right_size);
+    size_t right_size = static_cast<size_t>(end_ - right_);
+    memmove(new_right, right_, right_size);
 
-    ptrdiff_t bytes_reclaimed = _right - new_right;
-    _right = new_right;
-    _end = new_right + right_size;
+    ptrdiff_t bytes_reclaimed = right_ - new_right;
+    right_ = new_right;
+    end_ = new_right + right_size;
     return bytes_reclaimed;
   }
 
   // Move all pointers together
   // This funcion is called after a realloc.
   void movePointers(ptrdiff_t offset) {
-    _begin += offset;
-    _left += offset;
-    _right += offset;
-    _end += offset;
+    begin_ += offset;
+    left_ += offset;
+    right_ += offset;
+    end_ += offset;
   }
 
  private:
   void checkInvariants() {
-    ARDUINOJSON_ASSERT(_begin <= _left);
-    ARDUINOJSON_ASSERT(_left <= _right);
-    ARDUINOJSON_ASSERT(_right <= _end);
-    ARDUINOJSON_ASSERT(isAligned(_right));
+    ARDUINOJSON_ASSERT(begin_ <= left_);
+    ARDUINOJSON_ASSERT(left_ <= right_);
+    ARDUINOJSON_ASSERT(right_ <= end_);
+    ARDUINOJSON_ASSERT(isAligned(right_));
   }
 
 #if ARDUINOJSON_ENABLE_STRING_DEDUPLICATION
   template <typename TAdaptedString>
   const char* findString(const TAdaptedString& str) const {
     size_t n = str.size();
-    for (char* next = _begin; next + n < _left; ++next) {
+    for (char* next = begin_; next + n < left_; ++next) {
       if (next[n] == '\0' && stringEquals(str, adaptString(next, n)))
         return next;
 
@@ -192,11 +192,11 @@ class MemoryPool {
 
   char* allocString(size_t n) {
     if (!canAlloc(n)) {
-      _overflowed = true;
+      overflowed_ = true;
       return 0;
     }
-    char* s = _left;
-    _left += n;
+    char* s = left_;
+    left_ += n;
     checkInvariants();
     return s;
   }
@@ -208,15 +208,15 @@ class MemoryPool {
 
   void* allocRight(size_t bytes) {
     if (!canAlloc(bytes)) {
-      _overflowed = true;
+      overflowed_ = true;
       return 0;
     }
-    _right -= bytes;
-    return _right;
+    right_ -= bytes;
+    return right_;
   }
 
-  char *_begin, *_left, *_right, *_end;
-  bool _overflowed;
+  char *begin_, *left_, *right_, *end_;
+  bool overflowed_;
 };
 
 template <typename TAdaptedString, typename TCallback>
@@ -250,4 +250,4 @@ bool storeString(MemoryPool* pool, TAdaptedString str, TCallback callback) {
   return storeString(pool, str, str.storagePolicy(), callback);
 }
 
-}  // namespace ARDUINOJSON_NAMESPACE
+ARDUINOJSON_END_PRIVATE_NAMESPACE
diff --git a/neosensor/libraries/ArduinoJson/src/ArduinoJson/Misc/SafeBoolIdiom.hpp b/neosensor/libraries/ArduinoJson/src/ArduinoJson/Misc/SafeBoolIdiom.hpp
deleted file mode 100644
index ff4bf53a..00000000
--- a/neosensor/libraries/ArduinoJson/src/ArduinoJson/Misc/SafeBoolIdiom.hpp
+++ /dev/null
@@ -1,26 +0,0 @@
-// ArduinoJson - https://arduinojson.org
-// Copyright © 2014-2022, Benoit BLANCHON
-// MIT License
-
-#pragma once
-
-#include <ArduinoJson/Polyfills/type_traits.hpp>
-
-namespace ARDUINOJSON_NAMESPACE {
-
-template <typename T>
-class SafeBoolIdom {
- protected:
-  typedef void (T::*bool_type)() const;
-  void safeBoolHelper() const {}
-
-  static bool_type safe_true() {
-    return &SafeBoolIdom::safeBoolHelper;
-  }
-
-  static bool_type safe_false() {
-    return 0;
-  }
-};
-
-}  // namespace ARDUINOJSON_NAMESPACE
diff --git a/neosensor/libraries/ArduinoJson/src/ArduinoJson/Misc/SerializedValue.hpp b/neosensor/libraries/ArduinoJson/src/ArduinoJson/Misc/SerializedValue.hpp
index 217c6f3c..ae9abd3c 100644
--- a/neosensor/libraries/ArduinoJson/src/ArduinoJson/Misc/SerializedValue.hpp
+++ b/neosensor/libraries/ArduinoJson/src/ArduinoJson/Misc/SerializedValue.hpp
@@ -1,54 +1,54 @@
 // ArduinoJson - https://arduinojson.org
-// Copyright © 2014-2022, Benoit BLANCHON
+// Copyright © 2014-2023, Benoit BLANCHON
 // MIT License
 
 #pragma once
 
 #include <ArduinoJson/Strings/StringAdapters.hpp>
 
-namespace ARDUINOJSON_NAMESPACE {
+ARDUINOJSON_BEGIN_PUBLIC_NAMESPACE
 
 // A special type of data that can be used to insert pregenerated JSON portions.
 template <typename T>
 class SerializedValue {
  public:
-  explicit SerializedValue(T str) : _str(str) {}
+  explicit SerializedValue(T str) : str_(str) {}
   operator T() const {
-    return _str;
+    return str_;
   }
 
   const char* data() const {
-    return _str.c_str();
+    return str_.c_str();
   }
 
   size_t size() const {
     // CAUTION: the old Arduino String doesn't have size()
-    return _str.length();
+    return str_.length();
   }
 
  private:
-  T _str;
+  T str_;
 };
 
 template <typename TChar>
 class SerializedValue<TChar*> {
  public:
-  explicit SerializedValue(TChar* p, size_t n) : _data(p), _size(n) {}
+  explicit SerializedValue(TChar* p, size_t n) : data_(p), size_(n) {}
   operator TChar*() const {
-    return _data;
+    return data_;
   }
 
   TChar* data() const {
-    return _data;
+    return data_;
   }
 
   size_t size() const {
-    return _size;
+    return size_;
   }
 
  private:
-  TChar* _data;
-  size_t _size;
+  TChar* data_;
+  size_t size_;
 };
 
 template <typename T>
@@ -58,11 +58,12 @@ inline SerializedValue<T> serialized(T str) {
 
 template <typename TChar>
 inline SerializedValue<TChar*> serialized(TChar* p) {
-  return SerializedValue<TChar*>(p, adaptString(p).size());
+  return SerializedValue<TChar*>(p, detail::adaptString(p).size());
 }
 
 template <typename TChar>
 inline SerializedValue<TChar*> serialized(TChar* p, size_t n) {
   return SerializedValue<TChar*>(p, n);
 }
-}  // namespace ARDUINOJSON_NAMESPACE
+
+ARDUINOJSON_END_PUBLIC_NAMESPACE
diff --git a/neosensor/libraries/ArduinoJson/src/ArduinoJson/MsgPack/MsgPackDeserializer.hpp b/neosensor/libraries/ArduinoJson/src/ArduinoJson/MsgPack/MsgPackDeserializer.hpp
index 5013d2e2..41934295 100644
--- a/neosensor/libraries/ArduinoJson/src/ArduinoJson/MsgPack/MsgPackDeserializer.hpp
+++ b/neosensor/libraries/ArduinoJson/src/ArduinoJson/MsgPack/MsgPackDeserializer.hpp
@@ -1,5 +1,5 @@
 // ArduinoJson - https://arduinojson.org
-// Copyright © 2014-2022, Benoit BLANCHON
+// Copyright © 2014-2023, Benoit BLANCHON
 // MIT License
 
 #pragma once
@@ -11,30 +11,31 @@
 #include <ArduinoJson/Polyfills/type_traits.hpp>
 #include <ArduinoJson/Variant/VariantData.hpp>
 
-namespace ARDUINOJSON_NAMESPACE {
+ARDUINOJSON_BEGIN_PRIVATE_NAMESPACE
 
 template <typename TReader, typename TStringStorage>
 class MsgPackDeserializer {
  public:
   MsgPackDeserializer(MemoryPool* pool, TReader reader,
                       TStringStorage stringStorage)
-      : _pool(pool),
-        _reader(reader),
-        _stringStorage(stringStorage),
-        _foundSomething(false) {}
+      : pool_(pool),
+        reader_(reader),
+        stringStorage_(stringStorage),
+        foundSomething_(false) {}
 
   template <typename TFilter>
   DeserializationError parse(VariantData& variant, TFilter filter,
-                             NestingLimit nestingLimit) {
+                             DeserializationOption::NestingLimit nestingLimit) {
     DeserializationError::Code err;
     err = parseVariant(&variant, filter, nestingLimit);
-    return _foundSomething ? err : DeserializationError::EmptyInput;
+    return foundSomething_ ? err : DeserializationError::EmptyInput;
   }
 
  private:
   template <typename TFilter>
-  DeserializationError::Code parseVariant(VariantData* variant, TFilter filter,
-                                          NestingLimit nestingLimit) {
+  DeserializationError::Code parseVariant(
+      VariantData* variant, TFilter filter,
+      DeserializationOption::NestingLimit nestingLimit) {
     DeserializationError::Code err;
 
     uint8_t code = 0;  // TODO: why do we need to initialize this variable?
@@ -42,7 +43,7 @@ class MsgPackDeserializer {
     if (err)
       return err;
 
-    _foundSomething = true;
+    foundSomething_ = true;
 
     bool allowValue = filter.allowValue();
 
@@ -223,7 +224,7 @@ class MsgPackDeserializer {
   }
 
   DeserializationError::Code readByte(uint8_t& value) {
-    int c = _reader.read();
+    int c = reader_.read();
     if (c < 0)
       return DeserializationError::IncompleteInput;
     value = static_cast<uint8_t>(c);
@@ -231,7 +232,7 @@ class MsgPackDeserializer {
   }
 
   DeserializationError::Code readBytes(uint8_t* p, size_t n) {
-    if (_reader.readBytes(reinterpret_cast<char*>(p), n) == n)
+    if (reader_.readBytes(reinterpret_cast<char*>(p), n) == n)
       return DeserializationError::Ok;
     return DeserializationError::IncompleteInput;
   }
@@ -243,7 +244,7 @@ class MsgPackDeserializer {
 
   DeserializationError::Code skipBytes(size_t n) {
     for (; n; --n) {
-      if (_reader.read() < 0)
+      if (reader_.read() < 0)
         return DeserializationError::IncompleteInput;
     }
     return DeserializationError::Ok;
@@ -370,14 +371,14 @@ class MsgPackDeserializer {
     if (err)
       return err;
 
-    variant->setString(_stringStorage.save());
+    variant->setString(stringStorage_.save());
     return DeserializationError::Ok;
   }
 
   DeserializationError::Code readString(size_t n) {
     DeserializationError::Code err;
 
-    _stringStorage.startString();
+    stringStorage_.startString();
     for (; n; --n) {
       uint8_t c;
 
@@ -385,18 +386,19 @@ class MsgPackDeserializer {
       if (err)
         return err;
 
-      _stringStorage.append(static_cast<char>(c));
+      stringStorage_.append(static_cast<char>(c));
     }
 
-    if (!_stringStorage.isValid())
+    if (!stringStorage_.isValid())
       return DeserializationError::NoMemory;
 
     return DeserializationError::Ok;
   }
 
   template <typename TSize, typename TFilter>
-  DeserializationError::Code readArray(VariantData* variant, TFilter filter,
-                                       NestingLimit nestingLimit) {
+  DeserializationError::Code readArray(
+      VariantData* variant, TFilter filter,
+      DeserializationOption::NestingLimit nestingLimit) {
     DeserializationError::Code err;
     TSize size;
 
@@ -408,9 +410,9 @@ class MsgPackDeserializer {
   }
 
   template <typename TFilter>
-  DeserializationError::Code readArray(VariantData* variant, size_t n,
-                                       TFilter filter,
-                                       NestingLimit nestingLimit) {
+  DeserializationError::Code readArray(
+      VariantData* variant, size_t n, TFilter filter,
+      DeserializationOption::NestingLimit nestingLimit) {
     DeserializationError::Code err;
 
     if (nestingLimit.reached())
@@ -418,7 +420,13 @@ class MsgPackDeserializer {
 
     bool allowArray = filter.allowArray();
 
-    CollectionData* array = allowArray ? &variant->toArray() : 0;
+    CollectionData* array;
+    if (allowArray) {
+      ARDUINOJSON_ASSERT(variant != 0);
+      array = &variant->toArray();
+    } else {
+      array = 0;
+    }
 
     TFilter memberFilter = filter[0U];
 
@@ -426,7 +434,8 @@ class MsgPackDeserializer {
       VariantData* value;
 
       if (memberFilter.allow()) {
-        value = array->addElement(_pool);
+        ARDUINOJSON_ASSERT(array != 0);
+        value = array->addElement(pool_);
         if (!value)
           return DeserializationError::NoMemory;
       } else {
@@ -442,8 +451,9 @@ class MsgPackDeserializer {
   }
 
   template <typename TSize, typename TFilter>
-  DeserializationError::Code readObject(VariantData* variant, TFilter filter,
-                                        NestingLimit nestingLimit) {
+  DeserializationError::Code readObject(
+      VariantData* variant, TFilter filter,
+      DeserializationOption::NestingLimit nestingLimit) {
     DeserializationError::Code err;
     TSize size;
 
@@ -455,33 +465,39 @@ class MsgPackDeserializer {
   }
 
   template <typename TFilter>
-  DeserializationError::Code readObject(VariantData* variant, size_t n,
-                                        TFilter filter,
-                                        NestingLimit nestingLimit) {
+  DeserializationError::Code readObject(
+      VariantData* variant, size_t n, TFilter filter,
+      DeserializationOption::NestingLimit nestingLimit) {
     DeserializationError::Code err;
 
     if (nestingLimit.reached())
       return DeserializationError::TooDeep;
 
-    CollectionData* object = filter.allowObject() ? &variant->toObject() : 0;
+    CollectionData* object;
+    if (filter.allowObject()) {
+      ARDUINOJSON_ASSERT(variant != 0);
+      object = &variant->toObject();
+    } else {
+      object = 0;
+    }
 
     for (; n; --n) {
       err = readKey();
       if (err)
         return err;
 
-      JsonString key = _stringStorage.str();
+      JsonString key = stringStorage_.str();
       TFilter memberFilter = filter[key.c_str()];
       VariantData* member;
 
       if (memberFilter.allow()) {
-        ARDUINOJSON_ASSERT(object);
+        ARDUINOJSON_ASSERT(object != 0);
 
         // Save key in memory pool.
         // This MUST be done before adding the slot.
-        key = _stringStorage.save();
+        key = stringStorage_.save();
 
-        VariantSlot* slot = object->addSlot(_pool);
+        VariantSlot* slot = object->addSlot(pool_);
         if (!slot)
           return DeserializationError::NoMemory;
 
@@ -538,125 +554,32 @@ class MsgPackDeserializer {
     return skipBytes(size + 1U);
   }
 
-  MemoryPool* _pool;
-  TReader _reader;
-  TStringStorage _stringStorage;
-  bool _foundSomething;
+  MemoryPool* pool_;
+  TReader reader_;
+  TStringStorage stringStorage_;
+  bool foundSomething_;
 };
 
-// Parses a MessagePack input and puts the result in a JsonDocument.
-// https://arduinojson.org/v6/api/msgpack/deserializemsgpack/
-template <typename TString>
-DeserializationError deserializeMsgPack(
-    JsonDocument& doc, const TString& input,
-    NestingLimit nestingLimit = NestingLimit()) {
-  return deserialize<MsgPackDeserializer>(doc, input, nestingLimit,
-                                          AllowAllFilter());
-}
-
-// Parses a MessagePack input, filters, and puts the result in a JsonDocument.
-// https://arduinojson.org/v6/api/msgpack/deserializemsgpack/
-template <typename TString>
-DeserializationError deserializeMsgPack(
-    JsonDocument& doc, const TString& input, Filter filter,
-    NestingLimit nestingLimit = NestingLimit()) {
-  return deserialize<MsgPackDeserializer>(doc, input, nestingLimit, filter);
-}
+ARDUINOJSON_END_PRIVATE_NAMESPACE
 
-// Parses a MessagePack input, filters, and puts the result in a JsonDocument.
-// https://arduinojson.org/v6/api/msgpack/deserializemsgpack/
-template <typename TString>
-DeserializationError deserializeMsgPack(JsonDocument& doc, const TString& input,
-                                        NestingLimit nestingLimit,
-                                        Filter filter) {
-  return deserialize<MsgPackDeserializer>(doc, input, nestingLimit, filter);
-}
+ARDUINOJSON_BEGIN_PUBLIC_NAMESPACE
 
 // Parses a MessagePack input and puts the result in a JsonDocument.
 // https://arduinojson.org/v6/api/msgpack/deserializemsgpack/
-template <typename TStream>
-DeserializationError deserializeMsgPack(
-    JsonDocument& doc, TStream& input,
-    NestingLimit nestingLimit = NestingLimit()) {
-  return deserialize<MsgPackDeserializer>(doc, input, nestingLimit,
-                                          AllowAllFilter());
-}
-
-// Parses a MessagePack input, filters, and puts the result in a JsonDocument.
-// https://arduinojson.org/v6/api/msgpack/deserializemsgpack/
-template <typename TStream>
-DeserializationError deserializeMsgPack(
-    JsonDocument& doc, TStream& input, Filter filter,
-    NestingLimit nestingLimit = NestingLimit()) {
-  return deserialize<MsgPackDeserializer>(doc, input, nestingLimit, filter);
-}
-
-// Parses a MessagePack input, filters, and puts the result in a JsonDocument.
-// https://arduinojson.org/v6/api/msgpack/deserializemsgpack/
-template <typename TStream>
-DeserializationError deserializeMsgPack(JsonDocument& doc, TStream& input,
-                                        NestingLimit nestingLimit,
-                                        Filter filter) {
-  return deserialize<MsgPackDeserializer>(doc, input, nestingLimit, filter);
+template <typename... Args>
+DeserializationError deserializeMsgPack(JsonDocument& doc, Args&&... args) {
+  using namespace detail;
+  return deserialize<MsgPackDeserializer>(doc, detail::forward<Args>(args)...);
 }
 
 // Parses a MessagePack input and puts the result in a JsonDocument.
 // https://arduinojson.org/v6/api/msgpack/deserializemsgpack/
-template <typename TChar>
-DeserializationError deserializeMsgPack(
-    JsonDocument& doc, TChar* input,
-    NestingLimit nestingLimit = NestingLimit()) {
-  return deserialize<MsgPackDeserializer>(doc, input, nestingLimit,
-                                          AllowAllFilter());
-}
-
-// Parses a MessagePack input, filters, and puts the result in a JsonDocument.
-// https://arduinojson.org/v6/api/msgpack/deserializemsgpack/
-template <typename TChar>
-DeserializationError deserializeMsgPack(
-    JsonDocument& doc, TChar* input, Filter filter,
-    NestingLimit nestingLimit = NestingLimit()) {
-  return deserialize<MsgPackDeserializer>(doc, input, nestingLimit, filter);
-}
-
-// Parses a MessagePack input, filters, and puts the result in a JsonDocument.
-// https://arduinojson.org/v6/api/msgpack/deserializemsgpack/
-template <typename TChar>
-DeserializationError deserializeMsgPack(JsonDocument& doc, TChar* input,
-                                        NestingLimit nestingLimit,
-                                        Filter filter) {
-  return deserialize<MsgPackDeserializer>(doc, input, nestingLimit, filter);
-}
-
-// Parses a MessagePack input and puts the result in a JsonDocument.
-// https://arduinojson.org/v6/api/msgpack/deserializemsgpack/
-template <typename TChar>
-DeserializationError deserializeMsgPack(
-    JsonDocument& doc, TChar* input, size_t inputSize,
-    NestingLimit nestingLimit = NestingLimit()) {
-  return deserialize<MsgPackDeserializer>(doc, input, inputSize, nestingLimit,
-                                          AllowAllFilter());
-}
-
-// Parses a MessagePack input, filters, and puts the result in a JsonDocument.
-// https://arduinojson.org/v6/api/msgpack/deserializemsgpack/
-template <typename TChar>
-DeserializationError deserializeMsgPack(
-    JsonDocument& doc, TChar* input, size_t inputSize, Filter filter,
-    NestingLimit nestingLimit = NestingLimit()) {
-  return deserialize<MsgPackDeserializer>(doc, input, inputSize, nestingLimit,
-                                          filter);
-}
-
-// Parses a MessagePack input, filters, and puts the result in a JsonDocument.
-// https://arduinojson.org/v6/api/msgpack/deserializemsgpack/
-template <typename TChar>
+template <typename TChar, typename... Args>
 DeserializationError deserializeMsgPack(JsonDocument& doc, TChar* input,
-                                        size_t inputSize,
-                                        NestingLimit nestingLimit,
-                                        Filter filter) {
-  return deserialize<MsgPackDeserializer>(doc, input, inputSize, nestingLimit,
-                                          filter);
+                                        Args&&... args) {
+  using namespace detail;
+  return deserialize<MsgPackDeserializer>(doc, input,
+                                          detail::forward<Args>(args)...);
 }
 
-}  // namespace ARDUINOJSON_NAMESPACE
+ARDUINOJSON_END_PUBLIC_NAMESPACE
diff --git a/neosensor/libraries/ArduinoJson/src/ArduinoJson/MsgPack/MsgPackSerializer.hpp b/neosensor/libraries/ArduinoJson/src/ArduinoJson/MsgPack/MsgPackSerializer.hpp
index d2c44238..65769a16 100644
--- a/neosensor/libraries/ArduinoJson/src/ArduinoJson/MsgPack/MsgPackSerializer.hpp
+++ b/neosensor/libraries/ArduinoJson/src/ArduinoJson/MsgPack/MsgPackSerializer.hpp
@@ -1,5 +1,5 @@
 // ArduinoJson - https://arduinojson.org
-// Copyright © 2014-2022, Benoit BLANCHON
+// Copyright © 2014-2023, Benoit BLANCHON
 // MIT License
 
 #pragma once
@@ -12,14 +12,14 @@
 #include <ArduinoJson/Serialization/serialize.hpp>
 #include <ArduinoJson/Variant/VariantData.hpp>
 
-namespace ARDUINOJSON_NAMESPACE {
+ARDUINOJSON_BEGIN_PRIVATE_NAMESPACE
 
 template <typename TWriter>
 class MsgPackSerializer : public Visitor<size_t> {
  public:
   static const bool producesText = false;
 
-  MsgPackSerializer(TWriter writer) : _writer(writer) {}
+  MsgPackSerializer(TWriter writer) : writer_(writer) {}
 
   template <typename T>
   typename enable_if<sizeof(T) == 4, size_t>::type visitFloat(T value32) {
@@ -177,15 +177,15 @@ class MsgPackSerializer : public Visitor<size_t> {
 
  private:
   size_t bytesWritten() const {
-    return _writer.count();
+    return writer_.count();
   }
 
   void writeByte(uint8_t c) {
-    _writer.write(c);
+    writer_.write(c);
   }
 
   void writeBytes(const uint8_t* p, size_t n) {
-    _writer.write(p, n);
+    writer_.write(p, n);
   }
 
   template <typename T>
@@ -194,13 +194,18 @@ class MsgPackSerializer : public Visitor<size_t> {
     writeBytes(reinterpret_cast<uint8_t*>(&value), sizeof(value));
   }
 
-  CountingDecorator<TWriter> _writer;
+  CountingDecorator<TWriter> writer_;
 };
 
+ARDUINOJSON_END_PRIVATE_NAMESPACE
+
+ARDUINOJSON_BEGIN_PUBLIC_NAMESPACE
+
 // Produces a MessagePack document.
 // https://arduinojson.org/v6/api/msgpack/serializemsgpack/
 template <typename TDestination>
 inline size_t serializeMsgPack(JsonVariantConst source, TDestination& output) {
+  using namespace ArduinoJson::detail;
   return serialize<MsgPackSerializer>(source, output);
 }
 
@@ -208,13 +213,15 @@ inline size_t serializeMsgPack(JsonVariantConst source, TDestination& output) {
 // https://arduinojson.org/v6/api/msgpack/serializemsgpack/
 inline size_t serializeMsgPack(JsonVariantConst source, void* output,
                                size_t size) {
+  using namespace ArduinoJson::detail;
   return serialize<MsgPackSerializer>(source, output, size);
 }
 
 // Computes the length of the document that serializeMsgPack() produces.
 // https://arduinojson.org/v6/api/msgpack/measuremsgpack/
 inline size_t measureMsgPack(JsonVariantConst source) {
+  using namespace ArduinoJson::detail;
   return measure<MsgPackSerializer>(source);
 }
 
-}  // namespace ARDUINOJSON_NAMESPACE
+ARDUINOJSON_END_PUBLIC_NAMESPACE
diff --git a/neosensor/libraries/ArduinoJson/src/ArduinoJson/MsgPack/endianess.hpp b/neosensor/libraries/ArduinoJson/src/ArduinoJson/MsgPack/endianess.hpp
index 98e3156b..bb2dadd3 100644
--- a/neosensor/libraries/ArduinoJson/src/ArduinoJson/MsgPack/endianess.hpp
+++ b/neosensor/libraries/ArduinoJson/src/ArduinoJson/MsgPack/endianess.hpp
@@ -1,12 +1,12 @@
 // ArduinoJson - https://arduinojson.org
-// Copyright © 2014-2022, Benoit BLANCHON
+// Copyright © 2014-2023, Benoit BLANCHON
 // MIT License
 
 #pragma once
 
 #include <ArduinoJson/Polyfills/type_traits.hpp>
 
-namespace ARDUINOJSON_NAMESPACE {
+ARDUINOJSON_BEGIN_PRIVATE_NAMESPACE
 
 #if ARDUINOJSON_LITTLE_ENDIAN
 inline void swapBytes(uint8_t& a, uint8_t& b) {
@@ -43,4 +43,4 @@ template <typename T>
 inline void fixEndianess(T&) {}
 #endif
 
-}  // namespace ARDUINOJSON_NAMESPACE
+ARDUINOJSON_END_PRIVATE_NAMESPACE
diff --git a/neosensor/libraries/ArduinoJson/src/ArduinoJson/MsgPack/ieee754.hpp b/neosensor/libraries/ArduinoJson/src/ArduinoJson/MsgPack/ieee754.hpp
index 16f380ae..8513d493 100644
--- a/neosensor/libraries/ArduinoJson/src/ArduinoJson/MsgPack/ieee754.hpp
+++ b/neosensor/libraries/ArduinoJson/src/ArduinoJson/MsgPack/ieee754.hpp
@@ -1,12 +1,12 @@
 // ArduinoJson - https://arduinojson.org
-// Copyright © 2014-2022, Benoit BLANCHON
+// Copyright © 2014-2023, Benoit BLANCHON
 // MIT License
 
 #pragma once
 
 #include <ArduinoJson/Namespace.hpp>
 
-namespace ARDUINOJSON_NAMESPACE {
+ARDUINOJSON_BEGIN_PRIVATE_NAMESPACE
 
 inline void doubleToFloat(const uint8_t d[8], uint8_t f[4]) {
   f[0] = uint8_t((d[0] & 0xC0) | (d[0] << 3 & 0x3f) | (d[1] >> 5));
@@ -15,4 +15,4 @@ inline void doubleToFloat(const uint8_t d[8], uint8_t f[4]) {
   f[3] = uint8_t((d[3] << 3) | (d[4] >> 5));
 }
 
-}  // namespace ARDUINOJSON_NAMESPACE
+ARDUINOJSON_END_PRIVATE_NAMESPACE
diff --git a/neosensor/libraries/ArduinoJson/src/ArduinoJson/Namespace.hpp b/neosensor/libraries/ArduinoJson/src/ArduinoJson/Namespace.hpp
index ac4004eb..7f3b8b49 100644
--- a/neosensor/libraries/ArduinoJson/src/ArduinoJson/Namespace.hpp
+++ b/neosensor/libraries/ArduinoJson/src/ArduinoJson/Namespace.hpp
@@ -1,5 +1,5 @@
 // ArduinoJson - https://arduinojson.org
-// Copyright © 2014-2022, Benoit BLANCHON
+// Copyright © 2014-2023, Benoit BLANCHON
 // MIT License
 
 #pragma once
@@ -8,19 +8,36 @@
 #include <ArduinoJson/Polyfills/preprocessor.hpp>
 #include <ArduinoJson/version.hpp>
 
-#ifndef ARDUINOJSON_NAMESPACE
+#ifndef ARDUINOJSON_VERSION_NAMESPACE
 
-#  define ARDUINOJSON_NAMESPACE                                               \
-    ARDUINOJSON_CONCAT4(                                                      \
-        ARDUINOJSON_CONCAT4(ArduinoJson, ARDUINOJSON_VERSION_MAJOR,           \
+#  define ARDUINOJSON_VERSION_NAMESPACE                                       \
+    ARDUINOJSON_CONCAT3(                                                      \
+        ARDUINOJSON_CONCAT4(V, ARDUINOJSON_VERSION_MAJOR,                     \
                             ARDUINOJSON_VERSION_MINOR,                        \
                             ARDUINOJSON_VERSION_REVISION),                    \
-        _,                                                                    \
-        ARDUINOJSON_HEX_DIGIT(                                                \
+        ARDUINOJSON_BIN2ALPHA(                                                \
             ARDUINOJSON_ENABLE_PROGMEM, ARDUINOJSON_USE_LONG_LONG,            \
             ARDUINOJSON_USE_DOUBLE, ARDUINOJSON_ENABLE_STRING_DEDUPLICATION), \
-        ARDUINOJSON_HEX_DIGIT(                                                \
+        ARDUINOJSON_BIN2ALPHA(                                                \
             ARDUINOJSON_ENABLE_NAN, ARDUINOJSON_ENABLE_INFINITY,              \
             ARDUINOJSON_ENABLE_COMMENTS, ARDUINOJSON_DECODE_UNICODE))
 
 #endif
+
+#define ARDUINOJSON_BEGIN_PUBLIC_NAMESPACE \
+  namespace ArduinoJson {                  \
+  inline namespace ARDUINOJSON_VERSION_NAMESPACE {
+
+#define ARDUINOJSON_END_PUBLIC_NAMESPACE \
+  }                                      \
+  }
+
+#define ARDUINOJSON_BEGIN_PRIVATE_NAMESPACE        \
+  namespace ArduinoJson {                          \
+  inline namespace ARDUINOJSON_VERSION_NAMESPACE { \
+  namespace detail {
+
+#define ARDUINOJSON_END_PRIVATE_NAMESPACE \
+  }                                       \
+  }                                       \
+  }
diff --git a/neosensor/libraries/ArduinoJson/src/ArduinoJson/Numbers/FloatParts.hpp b/neosensor/libraries/ArduinoJson/src/ArduinoJson/Numbers/FloatParts.hpp
index 2f28ff75..214bc7a8 100644
--- a/neosensor/libraries/ArduinoJson/src/ArduinoJson/Numbers/FloatParts.hpp
+++ b/neosensor/libraries/ArduinoJson/src/ArduinoJson/Numbers/FloatParts.hpp
@@ -1,5 +1,5 @@
 // ArduinoJson - https://arduinojson.org
-// Copyright © 2014-2022, Benoit BLANCHON
+// Copyright © 2014-2023, Benoit BLANCHON
 // MIT License
 
 #pragma once
@@ -8,7 +8,7 @@
 #include <ArduinoJson/Numbers/FloatTraits.hpp>
 #include <ArduinoJson/Polyfills/math.hpp>
 
-namespace ARDUINOJSON_NAMESPACE {
+ARDUINOJSON_BEGIN_PRIVATE_NAMESPACE
 
 template <typename TFloat>
 struct FloatParts {
@@ -63,8 +63,8 @@ struct FloatParts {
 
     if (value >= ARDUINOJSON_POSITIVE_EXPONENTIATION_THRESHOLD) {
       for (; index >= 0; index--) {
-        if (value >= traits::positiveBinaryPowerOfTen(index)) {
-          value *= traits::negativeBinaryPowerOfTen(index);
+        if (value >= traits::positiveBinaryPowersOfTen()[index]) {
+          value *= traits::negativeBinaryPowersOfTen()[index];
           powersOf10 = int16_t(powersOf10 + bit);
         }
         bit >>= 1;
@@ -73,8 +73,8 @@ struct FloatParts {
 
     if (value > 0 && value <= ARDUINOJSON_NEGATIVE_EXPONENTIATION_THRESHOLD) {
       for (; index >= 0; index--) {
-        if (value < traits::negativeBinaryPowerOfTenPlusOne(index)) {
-          value *= traits::positiveBinaryPowerOfTen(index);
+        if (value < traits::negativeBinaryPowersOfTen()[index] * 10) {
+          value *= traits::positiveBinaryPowersOfTen()[index];
           powersOf10 = int16_t(powersOf10 - bit);
         }
         bit >>= 1;
@@ -84,4 +84,5 @@ struct FloatParts {
     return powersOf10;
   }
 };
-}  // namespace ARDUINOJSON_NAMESPACE
+
+ARDUINOJSON_END_PRIVATE_NAMESPACE
diff --git a/neosensor/libraries/ArduinoJson/src/ArduinoJson/Numbers/FloatTraits.hpp b/neosensor/libraries/ArduinoJson/src/ArduinoJson/Numbers/FloatTraits.hpp
index 4d5782b7..23abd7ff 100644
--- a/neosensor/libraries/ArduinoJson/src/ArduinoJson/Numbers/FloatTraits.hpp
+++ b/neosensor/libraries/ArduinoJson/src/ArduinoJson/Numbers/FloatTraits.hpp
@@ -1,5 +1,5 @@
 // ArduinoJson - https://arduinojson.org
-// Copyright © 2014-2022, Benoit BLANCHON
+// Copyright © 2014-2023, Benoit BLANCHON
 // MIT License
 
 #pragma once
@@ -10,11 +10,11 @@
 #include <ArduinoJson/Configuration.hpp>
 #include <ArduinoJson/Polyfills/alias_cast.hpp>
 #include <ArduinoJson/Polyfills/math.hpp>
+#include <ArduinoJson/Polyfills/pgmspace_generic.hpp>
 #include <ArduinoJson/Polyfills/preprocessor.hpp>
-#include <ArduinoJson/Polyfills/static_array.hpp>
 #include <ArduinoJson/Polyfills/type_traits.hpp>
 
-namespace ARDUINOJSON_NAMESPACE {
+ARDUINOJSON_BEGIN_PRIVATE_NAMESPACE
 
 template <typename T, size_t = sizeof(T)>
 struct FloatTraits {};
@@ -29,92 +29,50 @@ struct FloatTraits<T, 8 /*64bits*/> {
   typedef int16_t exponent_type;
   static const exponent_type exponent_max = 308;
 
-  template <typename TExponent>
-  static T make_float(T m, TExponent e) {
-    if (e > 0) {
-      for (uint8_t index = 0; e != 0; index++) {
-        if (e & 1)
-          m *= positiveBinaryPowerOfTen(index);
-        e >>= 1;
-      }
-    } else {
-      e = TExponent(-e);
-      for (uint8_t index = 0; e != 0; index++) {
-        if (e & 1)
-          m *= negativeBinaryPowerOfTen(index);
-        e >>= 1;
-      }
-    }
-    return m;
-  }
-
-  static T positiveBinaryPowerOfTen(int index) {
-    ARDUINOJSON_DEFINE_STATIC_ARRAY(  //
-        uint32_t, factors,
-        ARDUINOJSON_EXPAND18({
-            0x40240000, 0x00000000,  // 1e1
-            0x40590000, 0x00000000,  // 1e2
-            0x40C38800, 0x00000000,  // 1e4
-            0x4197D784, 0x00000000,  // 1e8
-            0x4341C379, 0x37E08000,  // 1e16
-            0x4693B8B5, 0xB5056E17,  // 1e32
-            0x4D384F03, 0xE93FF9F5,  // 1e64
-            0x5A827748, 0xF9301D32,  // 1e128
-            0x75154FDD, 0x7F73BF3C   // 1e256
-        }));
-    return forge(
-        ARDUINOJSON_READ_STATIC_ARRAY(uint32_t, factors, 2 * index),
-        ARDUINOJSON_READ_STATIC_ARRAY(uint32_t, factors, 2 * index + 1));
-  }
-
-  static T negativeBinaryPowerOfTen(int index) {
-    ARDUINOJSON_DEFINE_STATIC_ARRAY(  //
-        uint32_t, factors,
-        ARDUINOJSON_EXPAND18({
-            0x3FB99999, 0x9999999A,  // 1e-1
-            0x3F847AE1, 0x47AE147B,  // 1e-2
-            0x3F1A36E2, 0xEB1C432D,  // 1e-4
-            0x3E45798E, 0xE2308C3A,  // 1e-8
-            0x3C9CD2B2, 0x97D889BC,  // 1e-16
-            0x3949F623, 0xD5A8A733,  // 1e-32
-            0x32A50FFD, 0x44F4A73D,  // 1e-64
-            0x255BBA08, 0xCF8C979D,  // 1e-128
-            0x0AC80628, 0x64AC6F43   // 1e-256
-        }));
-    return forge(
-        ARDUINOJSON_READ_STATIC_ARRAY(uint32_t, factors, 2 * index),
-        ARDUINOJSON_READ_STATIC_ARRAY(uint32_t, factors, 2 * index + 1));
-  }
-
-  static T negativeBinaryPowerOfTenPlusOne(int index) {
-    ARDUINOJSON_DEFINE_STATIC_ARRAY(  //
-        uint32_t, factors,
-        ARDUINOJSON_EXPAND18({
-            0x3FF00000, 0x00000000,  // 1e0
-            0x3FB99999, 0x9999999A,  // 1e-1
-            0x3F50624D, 0xD2F1A9FC,  // 1e-3
-            0x3E7AD7F2, 0x9ABCAF48,  // 1e-7
-            0x3CD203AF, 0x9EE75616,  // 1e-15
-            0x398039D6, 0x65896880,  // 1e-31
-            0x32DA53FC, 0x9631D10D,  // 1e-63
-            0x25915445, 0x81B7DEC2,  // 1e-127
-            0x0AFE07B2, 0x7DD78B14   // 1e-255
-        }));
-    return forge(
-        ARDUINOJSON_READ_STATIC_ARRAY(uint32_t, factors, 2 * index),
-        ARDUINOJSON_READ_STATIC_ARRAY(uint32_t, factors, 2 * index + 1));
+  static pgm_ptr<T> positiveBinaryPowersOfTen() {
+    ARDUINOJSON_DEFINE_PROGMEM_ARRAY(  //
+        uint64_t, factors,
+        {
+            0x4024000000000000,  // 1e1
+            0x4059000000000000,  // 1e2
+            0x40C3880000000000,  // 1e4
+            0x4197D78400000000,  // 1e8
+            0x4341C37937E08000,  // 1e16
+            0x4693B8B5B5056E17,  // 1e32
+            0x4D384F03E93FF9F5,  // 1e64
+            0x5A827748F9301D32,  // 1e128
+            0x75154FDD7F73BF3C,  // 1e256
+        });
+    return pgm_ptr<T>(reinterpret_cast<const T*>(factors));
+  }
+
+  static pgm_ptr<T> negativeBinaryPowersOfTen() {
+    ARDUINOJSON_DEFINE_PROGMEM_ARRAY(  //
+        uint64_t, factors,
+        {
+            0x3FB999999999999A,  // 1e-1
+            0x3F847AE147AE147B,  // 1e-2
+            0x3F1A36E2EB1C432D,  // 1e-4
+            0x3E45798EE2308C3A,  // 1e-8
+            0x3C9CD2B297D889BC,  // 1e-16
+            0x3949F623D5A8A733,  // 1e-32
+            0x32A50FFD44F4A73D,  // 1e-64
+            0x255BBA08CF8C979D,  // 1e-128
+            0x0AC8062864AC6F43   // 1e-256
+        });
+    return pgm_ptr<T>(reinterpret_cast<const T*>(factors));
   }
 
   static T nan() {
-    return forge(0x7ff80000, 0x00000000);
+    return forge(0x7ff8000000000000);
   }
 
   static T inf() {
-    return forge(0x7ff00000, 0x00000000);
+    return forge(0x7ff0000000000000);
   }
 
   static T highest() {
-    return forge(0x7FEFFFFF, 0xFFFFFFFF);
+    return forge(0x7FEFFFFFFFFFFFFF);
   }
 
   template <typename TOut>  // int64_t
@@ -122,7 +80,7 @@ struct FloatTraits<T, 8 /*64bits*/> {
       typename enable_if<is_integral<TOut>::value && is_signed<TOut>::value &&
                              sizeof(TOut) == 8,
                          signed>::type* = 0) {
-    return forge(0x43DFFFFF, 0xFFFFFFFF);  //  9.2233720368547748e+18
+    return forge(0x43DFFFFFFFFFFFFF);  //  9.2233720368547748e+18
   }
 
   template <typename TOut>  // uint64_t
@@ -130,18 +88,18 @@ struct FloatTraits<T, 8 /*64bits*/> {
       typename enable_if<is_integral<TOut>::value && is_unsigned<TOut>::value &&
                              sizeof(TOut) == 8,
                          unsigned>::type* = 0) {
-    return forge(0x43EFFFFF, 0xFFFFFFFF);  //  1.8446744073709549568e+19
+    return forge(0x43EFFFFFFFFFFFFF);  //  1.8446744073709549568e+19
   }
 
   static T lowest() {
-    return forge(0xFFEFFFFF, 0xFFFFFFFF);
+    return forge(0xFFEFFFFFFFFFFFFF);
   }
 
   // constructs a double floating point values from its binary representation
   // we use this function to workaround platforms with single precision literals
   // (for example, when -fsingle-precision-constant is passed to GCC)
-  static T forge(uint32_t msb, uint32_t lsb) {
-    return alias_cast<T>((uint64_t(msb) << 32) | lsb);
+  static T forge(uint64_t bits) {
+    return alias_cast<T>(bits);
   }
 };
 
@@ -155,62 +113,30 @@ struct FloatTraits<T, 4 /*32bits*/> {
   typedef int8_t exponent_type;
   static const exponent_type exponent_max = 38;
 
-  template <typename TExponent>
-  static T make_float(T m, TExponent e) {
-    if (e > 0) {
-      for (uint8_t index = 0; e != 0; index++) {
-        if (e & 1)
-          m *= positiveBinaryPowerOfTen(index);
-        e >>= 1;
-      }
-    } else {
-      e = -e;
-      for (uint8_t index = 0; e != 0; index++) {
-        if (e & 1)
-          m *= negativeBinaryPowerOfTen(index);
-        e >>= 1;
-      }
-    }
-    return m;
-  }
-
-  static T positiveBinaryPowerOfTen(int index) {
-    ARDUINOJSON_DEFINE_STATIC_ARRAY(uint32_t, factors,
-                                    ARDUINOJSON_EXPAND6({
-                                        0x41200000,  // 1e1f
-                                        0x42c80000,  // 1e2f
-                                        0x461c4000,  // 1e4f
-                                        0x4cbebc20,  // 1e8f
-                                        0x5a0e1bca,  // 1e16f
-                                        0x749dc5ae   // 1e32f
-                                    }));
-    return forge(ARDUINOJSON_READ_STATIC_ARRAY(uint32_t, factors, index));
-  }
-
-  static T negativeBinaryPowerOfTen(int index) {
-    ARDUINOJSON_DEFINE_STATIC_ARRAY(uint32_t, factors,
-                                    ARDUINOJSON_EXPAND6({
-                                        0x3dcccccd,  // 1e-1f
-                                        0x3c23d70a,  // 1e-2f
-                                        0x38d1b717,  // 1e-4f
-                                        0x322bcc77,  // 1e-8f
-                                        0x24e69595,  // 1e-16f
-                                        0x0a4fb11f   // 1e-32f
-                                    }));
-    return forge(ARDUINOJSON_READ_STATIC_ARRAY(uint32_t, factors, index));
-  }
-
-  static T negativeBinaryPowerOfTenPlusOne(int index) {
-    ARDUINOJSON_DEFINE_STATIC_ARRAY(uint32_t, factors,
-                                    ARDUINOJSON_EXPAND6({
-                                        0x3f800000,  // 1e0f
-                                        0x3dcccccd,  // 1e-1f
-                                        0x3a83126f,  // 1e-3f
-                                        0x33d6bf95,  // 1e-7f
-                                        0x26901d7d,  // 1e-15f
-                                        0x0c01ceb3   // 1e-31f
-                                    }));
-    return forge(ARDUINOJSON_READ_STATIC_ARRAY(uint32_t, factors, index));
+  static pgm_ptr<T> positiveBinaryPowersOfTen() {
+    ARDUINOJSON_DEFINE_PROGMEM_ARRAY(uint32_t, factors,
+                                     {
+                                         0x41200000,  // 1e1f
+                                         0x42c80000,  // 1e2f
+                                         0x461c4000,  // 1e4f
+                                         0x4cbebc20,  // 1e8f
+                                         0x5a0e1bca,  // 1e16f
+                                         0x749dc5ae   // 1e32f
+                                     });
+    return pgm_ptr<T>(reinterpret_cast<const T*>(factors));
+  }
+
+  static pgm_ptr<T> negativeBinaryPowersOfTen() {
+    ARDUINOJSON_DEFINE_PROGMEM_ARRAY(uint32_t, factors,
+                                     {
+                                         0x3dcccccd,  // 1e-1f
+                                         0x3c23d70a,  // 1e-2f
+                                         0x38d1b717,  // 1e-4f
+                                         0x322bcc77,  // 1e-8f
+                                         0x24e69595,  // 1e-16f
+                                         0x0a4fb11f   // 1e-32f
+                                     });
+    return pgm_ptr<T>(reinterpret_cast<const T*>(factors));
   }
 
   static T forge(uint32_t bits) {
@@ -265,4 +191,22 @@ struct FloatTraits<T, 4 /*32bits*/> {
     return forge(0xFf7fffff);
   }
 };
-}  // namespace ARDUINOJSON_NAMESPACE
+
+template <typename TFloat, typename TExponent>
+inline TFloat make_float(TFloat m, TExponent e) {
+  using traits = FloatTraits<TFloat>;
+
+  auto powersOfTen = e > 0 ? traits::positiveBinaryPowersOfTen()
+                           : traits::negativeBinaryPowersOfTen();
+  if (e <= 0)
+    e = TExponent(-e);
+
+  for (uint8_t index = 0; e != 0; index++) {
+    if (e & 1)
+      m *= powersOfTen[index];
+    e >>= 1;
+  }
+  return m;
+}
+
+ARDUINOJSON_END_PRIVATE_NAMESPACE
diff --git a/neosensor/libraries/ArduinoJson/src/ArduinoJson/Numbers/JsonFloat.hpp b/neosensor/libraries/ArduinoJson/src/ArduinoJson/Numbers/JsonFloat.hpp
index a88439fe..a1259d07 100644
--- a/neosensor/libraries/ArduinoJson/src/ArduinoJson/Numbers/JsonFloat.hpp
+++ b/neosensor/libraries/ArduinoJson/src/ArduinoJson/Numbers/JsonFloat.hpp
@@ -1,5 +1,5 @@
 // ArduinoJson - https://arduinojson.org
-// Copyright © 2014-2022, Benoit BLANCHON
+// Copyright © 2014-2023, Benoit BLANCHON
 // MIT License
 
 #pragma once
@@ -7,11 +7,12 @@
 #include <ArduinoJson/Configuration.hpp>
 #include <ArduinoJson/Namespace.hpp>
 
-namespace ARDUINOJSON_NAMESPACE {
+ARDUINOJSON_BEGIN_PUBLIC_NAMESPACE
 
 #if ARDUINOJSON_USE_DOUBLE
 typedef double JsonFloat;
 #else
 typedef float JsonFloat;
 #endif
-}  // namespace ARDUINOJSON_NAMESPACE
+
+ARDUINOJSON_END_PUBLIC_NAMESPACE
diff --git a/neosensor/libraries/ArduinoJson/src/ArduinoJson/Numbers/JsonInteger.hpp b/neosensor/libraries/ArduinoJson/src/ArduinoJson/Numbers/JsonInteger.hpp
index ccf852bd..91005653 100644
--- a/neosensor/libraries/ArduinoJson/src/ArduinoJson/Numbers/JsonInteger.hpp
+++ b/neosensor/libraries/ArduinoJson/src/ArduinoJson/Numbers/JsonInteger.hpp
@@ -1,5 +1,5 @@
 // ArduinoJson - https://arduinojson.org
-// Copyright © 2014-2022, Benoit BLANCHON
+// Copyright © 2014-2023, Benoit BLANCHON
 // MIT License
 
 #pragma once
@@ -9,7 +9,7 @@
 
 #include <stdint.h>  // int64_t
 
-namespace ARDUINOJSON_NAMESPACE {
+ARDUINOJSON_BEGIN_PUBLIC_NAMESPACE
 
 #if ARDUINOJSON_USE_LONG_LONG
 typedef int64_t JsonInteger;
@@ -19,14 +19,10 @@ typedef long JsonInteger;
 typedef unsigned long JsonUInt;
 #endif
 
-}  // namespace ARDUINOJSON_NAMESPACE
+ARDUINOJSON_END_PUBLIC_NAMESPACE
 
-#if ARDUINOJSON_HAS_LONG_LONG && !ARDUINOJSON_USE_LONG_LONG
-#  define ARDUINOJSON_ASSERT_INTEGER_TYPE_IS_SUPPORTED(T)                  \
-    static_assert(sizeof(T) <= sizeof(ARDUINOJSON_NAMESPACE::JsonInteger), \
-                  "To use 64-bit integers with ArduinoJson, you must set " \
-                  "ARDUINOJSON_USE_LONG_LONG to 1. See "                   \
-                  "https://arduinojson.org/v6/api/config/use_long_long/");
-#else
-#  define ARDUINOJSON_ASSERT_INTEGER_TYPE_IS_SUPPORTED(T)
-#endif
+#define ARDUINOJSON_ASSERT_INTEGER_TYPE_IS_SUPPORTED(T)                  \
+  static_assert(sizeof(T) <= sizeof(ArduinoJson::JsonInteger),           \
+                "To use 64-bit integers with ArduinoJson, you must set " \
+                "ARDUINOJSON_USE_LONG_LONG to 1. See "                   \
+                "https://arduinojson.org/v6/api/config/use_long_long/");
diff --git a/neosensor/libraries/ArduinoJson/src/ArduinoJson/Numbers/arithmeticCompare.hpp b/neosensor/libraries/ArduinoJson/src/ArduinoJson/Numbers/arithmeticCompare.hpp
index 515bc4e6..e706fd94 100644
--- a/neosensor/libraries/ArduinoJson/src/ArduinoJson/Numbers/arithmeticCompare.hpp
+++ b/neosensor/libraries/ArduinoJson/src/ArduinoJson/Numbers/arithmeticCompare.hpp
@@ -1,5 +1,5 @@
 // ArduinoJson - https://arduinojson.org
-// Copyright © 2014-2022, Benoit BLANCHON
+// Copyright © 2014-2023, Benoit BLANCHON
 // MIT License
 
 #pragma once
@@ -7,7 +7,7 @@
 #include <ArduinoJson/Numbers/JsonInteger.hpp>
 #include <ArduinoJson/Polyfills/type_traits.hpp>
 
-namespace ARDUINOJSON_NAMESPACE {
+ARDUINOJSON_BEGIN_PRIVATE_NAMESPACE
 
 enum CompareResult {
   COMPARE_RESULT_DIFFER = 0,
@@ -33,10 +33,7 @@ template <typename T1, typename T2>
 CompareResult arithmeticCompare(
     const T1& lhs, const T2& rhs,
     typename enable_if<is_integral<T1>::value && is_integral<T2>::value &&
-                           sizeof(T1) < sizeof(T2),
-                       int  // Using int instead of void to avoid C2572 on
-                            // Visual Studio 2012, 2013, and 2015
-                       >::type* = 0) {
+                       sizeof(T1) < sizeof(T2)>::type* = 0) {
   return arithmeticCompare<T2>(static_cast<T2>(lhs), rhs);
 }
 
@@ -120,4 +117,4 @@ CompareResult arithmeticCompareNegateRight(
   return arithmeticCompare(static_cast<T1>(rhs), -lhs);
 }
 
-}  // namespace ARDUINOJSON_NAMESPACE
+ARDUINOJSON_END_PRIVATE_NAMESPACE
diff --git a/neosensor/libraries/ArduinoJson/src/ArduinoJson/Numbers/convertNumber.hpp b/neosensor/libraries/ArduinoJson/src/ArduinoJson/Numbers/convertNumber.hpp
index 9a78bdd5..4fbf8655 100644
--- a/neosensor/libraries/ArduinoJson/src/ArduinoJson/Numbers/convertNumber.hpp
+++ b/neosensor/libraries/ArduinoJson/src/ArduinoJson/Numbers/convertNumber.hpp
@@ -1,5 +1,5 @@
 // ArduinoJson - https://arduinojson.org
-// Copyright © 2014-2022, Benoit BLANCHON
+// Copyright © 2014-2023, Benoit BLANCHON
 // MIT License
 
 #pragma once
@@ -8,9 +8,7 @@
 #  pragma clang diagnostic push
 #  pragma clang diagnostic ignored "-Wconversion"
 #elif defined(__GNUC__)
-#  if __GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 6)
-#    pragma GCC diagnostic push
-#  endif
+#  pragma GCC diagnostic push
 #  pragma GCC diagnostic ignored "-Wconversion"
 #endif
 
@@ -19,7 +17,7 @@
 #include <ArduinoJson/Polyfills/limits.hpp>
 #include <ArduinoJson/Polyfills/type_traits.hpp>
 
-namespace ARDUINOJSON_NAMESPACE {
+ARDUINOJSON_BEGIN_PRIVATE_NAMESPACE
 
 // uint32 -> int32
 // uint64 -> int32
@@ -128,12 +126,10 @@ template <typename TOut, typename TIn>
 TOut convertNumber(TIn value) {
   return canConvertNumber<TOut>(value) ? TOut(value) : 0;
 }
-}  // namespace ARDUINOJSON_NAMESPACE
+ARDUINOJSON_END_PRIVATE_NAMESPACE
 
 #if defined(__clang__)
 #  pragma clang diagnostic pop
 #elif defined(__GNUC__)
-#  if __GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 6)
-#    pragma GCC diagnostic pop
-#  endif
+#  pragma GCC diagnostic pop
 #endif
diff --git a/neosensor/libraries/ArduinoJson/src/ArduinoJson/Numbers/parseNumber.hpp b/neosensor/libraries/ArduinoJson/src/ArduinoJson/Numbers/parseNumber.hpp
index 05fca733..4cec1a69 100644
--- a/neosensor/libraries/ArduinoJson/src/ArduinoJson/Numbers/parseNumber.hpp
+++ b/neosensor/libraries/ArduinoJson/src/ArduinoJson/Numbers/parseNumber.hpp
@@ -1,5 +1,5 @@
 // ArduinoJson - https://arduinojson.org
-// Copyright © 2014-2022, Benoit BLANCHON
+// Copyright © 2014-2023, Benoit BLANCHON
 // MIT License
 
 #pragma once
@@ -13,7 +13,7 @@
 #include <ArduinoJson/Variant/Converter.hpp>
 #include <ArduinoJson/Variant/VariantData.hpp>
 
-namespace ARDUINOJSON_NAMESPACE {
+ARDUINOJSON_BEGIN_PRIVATE_NAMESPACE
 
 template <typename A, typename B>
 struct choose_largest : conditional<(sizeof(A) > sizeof(B)), A, B> {};
@@ -137,7 +137,7 @@ inline bool parseNumber(const char* s, VariantData& result) {
     return false;
 
   JsonFloat final_result =
-      traits::make_float(static_cast<JsonFloat>(mantissa), exponent);
+      make_float(static_cast<JsonFloat>(mantissa), exponent);
 
   result.setFloat(is_negative ? -final_result : final_result);
   return true;
@@ -146,8 +146,7 @@ inline bool parseNumber(const char* s, VariantData& result) {
 template <typename T>
 inline T parseNumber(const char* s) {
   VariantData value;
-  value.init();  // VariantData is a POD, so it has no constructor
   parseNumber(s, value);
   return Converter<T>::fromJson(JsonVariantConst(&value));
 }
-}  // namespace ARDUINOJSON_NAMESPACE
+ARDUINOJSON_END_PRIVATE_NAMESPACE
diff --git a/neosensor/libraries/ArduinoJson/src/ArduinoJson/Object/JsonObject.hpp b/neosensor/libraries/ArduinoJson/src/ArduinoJson/Object/JsonObject.hpp
index 0df210d8..7cdc1c76 100644
--- a/neosensor/libraries/ArduinoJson/src/ArduinoJson/Object/JsonObject.hpp
+++ b/neosensor/libraries/ArduinoJson/src/ArduinoJson/Object/JsonObject.hpp
@@ -1,5 +1,5 @@
 // ArduinoJson - https://arduinojson.org
-// Copyright © 2014-2022, Benoit BLANCHON
+// Copyright © 2014-2023, Benoit BLANCHON
 // MIT License
 
 #pragma once
@@ -7,74 +7,74 @@
 #include <ArduinoJson/Object/JsonObjectConst.hpp>
 #include <ArduinoJson/Object/MemberProxy.hpp>
 
-namespace ARDUINOJSON_NAMESPACE {
+ARDUINOJSON_BEGIN_PUBLIC_NAMESPACE
 
 class JsonArray;
 
 // A reference to an object in a JsonDocument.
 // https://arduinojson.org/v6/api/jsonobject/
-class JsonObject : public VariantOperators<JsonObject> {
-  friend class VariantAttorney;
+class JsonObject : public detail::VariantOperators<JsonObject> {
+  friend class detail::VariantAttorney;
 
  public:
   typedef JsonObjectIterator iterator;
 
   // Creates an unbound reference.
-  FORCE_INLINE JsonObject() : _data(0), _pool(0) {}
+  FORCE_INLINE JsonObject() : data_(0), pool_(0) {}
 
   // INTERNAL USE ONLY
-  FORCE_INLINE JsonObject(MemoryPool* buf, CollectionData* data)
-      : _data(data), _pool(buf) {}
+  FORCE_INLINE JsonObject(detail::MemoryPool* buf, detail::CollectionData* data)
+      : data_(data), pool_(buf) {}
 
   operator JsonVariant() const {
-    void* data = _data;  // prevent warning cast-align
-    return JsonVariant(_pool, reinterpret_cast<VariantData*>(data));
+    void* data = data_;  // prevent warning cast-align
+    return JsonVariant(pool_, reinterpret_cast<detail::VariantData*>(data));
   }
 
   operator JsonObjectConst() const {
-    return JsonObjectConst(_data);
+    return JsonObjectConst(data_);
   }
 
   operator JsonVariantConst() const {
-    return JsonVariantConst(collectionToVariant(_data));
+    return JsonVariantConst(collectionToVariant(data_));
   }
 
   // Returns true if the reference is unbound.
   // https://arduinojson.org/v6/api/jsonobject/isnull/
   FORCE_INLINE bool isNull() const {
-    return _data == 0;
+    return data_ == 0;
   }
 
   // Returns true if the reference is bound.
   // https://arduinojson.org/v6/api/jsonobject/isnull/
   FORCE_INLINE operator bool() const {
-    return _data != 0;
+    return data_ != 0;
   }
 
   // Returns the number of bytes occupied by the object.
   // https://arduinojson.org/v6/api/jsonobject/memoryusage/
   FORCE_INLINE size_t memoryUsage() const {
-    return _data ? _data->memoryUsage() : 0;
+    return data_ ? data_->memoryUsage() : 0;
   }
 
   // Returns the depth (nesting level) of the object.
   // https://arduinojson.org/v6/api/jsonobject/nesting/
   FORCE_INLINE size_t nesting() const {
-    return variantNesting(collectionToVariant(_data));
+    return variantNesting(collectionToVariant(data_));
   }
 
   // Returns the number of members in the object.
   // https://arduinojson.org/v6/api/jsonobject/size/
   FORCE_INLINE size_t size() const {
-    return _data ? _data->size() : 0;
+    return data_ ? data_->size() : 0;
   }
 
   // Returns an iterator to the first key-value pair of the object.
   // https://arduinojson.org/v6/api/jsonobject/begin/
   FORCE_INLINE iterator begin() const {
-    if (!_data)
+    if (!data_)
       return iterator();
-    return iterator(_pool, _data->head());
+    return iterator(pool_, data_->head());
   }
 
   // Returns an iterator following the last key-value pair of the object.
@@ -87,49 +87,51 @@ class JsonObject : public VariantOperators<JsonObject> {
   // ⚠️ Doesn't release the memory associated with the removed members.
   // https://arduinojson.org/v6/api/jsonobject/clear/
   void clear() const {
-    if (!_data)
+    if (!data_)
       return;
-    _data->clear();
+    data_->clear();
   }
 
   // Copies an object.
   // https://arduinojson.org/v6/api/jsonobject/set/
   FORCE_INLINE bool set(JsonObjectConst src) {
-    if (!_data || !src._data)
+    if (!data_ || !src.data_)
       return false;
-    return _data->copyFrom(*src._data, _pool);
+    return data_->copyFrom(*src.data_, pool_);
   }
 
   // Compares the content of two objects.
   FORCE_INLINE bool operator==(JsonObject rhs) const {
-    return JsonObjectConst(_data) == JsonObjectConst(rhs._data);
+    return JsonObjectConst(data_) == JsonObjectConst(rhs.data_);
   }
 
   // Gets or sets the member with specified key.
   // https://arduinojson.org/v6/api/jsonobject/subscript/
   template <typename TString>
-  FORCE_INLINE typename enable_if<IsString<TString>::value,
-                                  MemberProxy<JsonObject, TString> >::type
-  operator[](const TString& key) const {
-    return MemberProxy<JsonObject, TString>(*this, key);
+  FORCE_INLINE
+      typename detail::enable_if<detail::IsString<TString>::value,
+                                 detail::MemberProxy<JsonObject, TString>>::type
+      operator[](const TString& key) const {
+    return {*this, key};
   }
 
   // Gets or sets the member with specified key.
   // https://arduinojson.org/v6/api/jsonobject/subscript/
   template <typename TChar>
-  FORCE_INLINE typename enable_if<IsString<TChar*>::value,
-                                  MemberProxy<JsonObject, TChar*> >::type
-  operator[](TChar* key) const {
-    return MemberProxy<JsonObject, TChar*>(*this, key);
+  FORCE_INLINE
+      typename detail::enable_if<detail::IsString<TChar*>::value,
+                                 detail::MemberProxy<JsonObject, TChar*>>::type
+      operator[](TChar* key) const {
+    return {*this, key};
   }
 
   // Removes the member at the specified iterator.
   // ⚠️ Doesn't release the memory associated with the removed member.
   // https://arduinojson.org/v6/api/jsonobject/remove/
   FORCE_INLINE void remove(iterator it) const {
-    if (!_data)
+    if (!data_)
       return;
-    _data->removeSlot(it._slot);
+    data_->removeSlot(it.slot_);
   }
 
   // Removes the member with the specified key.
@@ -137,7 +139,7 @@ class JsonObject : public VariantOperators<JsonObject> {
   // https://arduinojson.org/v6/api/jsonobject/remove/
   template <typename TString>
   FORCE_INLINE void remove(const TString& key) const {
-    removeMember(adaptString(key));
+    removeMember(detail::adaptString(key));
   }
 
   // Removes the member with the specified key.
@@ -145,23 +147,25 @@ class JsonObject : public VariantOperators<JsonObject> {
   // https://arduinojson.org/v6/api/jsonobject/remove/
   template <typename TChar>
   FORCE_INLINE void remove(TChar* key) const {
-    removeMember(adaptString(key));
+    removeMember(detail::adaptString(key));
   }
 
   // Returns true if the object contains the specified key.
   // https://arduinojson.org/v6/api/jsonobject/containskey/
   template <typename TString>
-  FORCE_INLINE typename enable_if<IsString<TString>::value, bool>::type
-  containsKey(const TString& key) const {
-    return getMember(adaptString(key)) != 0;
+  FORCE_INLINE
+      typename detail::enable_if<detail::IsString<TString>::value, bool>::type
+      containsKey(const TString& key) const {
+    return getMember(detail::adaptString(key)) != 0;
   }
 
   // Returns true if the object contains the specified key.
   // https://arduinojson.org/v6/api/jsonobject/containskey/
   template <typename TChar>
-  FORCE_INLINE typename enable_if<IsString<TChar*>::value, bool>::type
-  containsKey(TChar* key) const {
-    return getMember(adaptString(key)) != 0;
+  FORCE_INLINE
+      typename detail::enable_if<detail::IsString<TChar*>::value, bool>::type
+      containsKey(TChar* key) const {
+    return getMember(detail::adaptString(key)) != 0;
   }
 
   // Creates an array and adds it to the object.
@@ -189,49 +193,49 @@ class JsonObject : public VariantOperators<JsonObject> {
   }
 
  private:
-  MemoryPool* getPool() const {
-    return _pool;
+  detail::MemoryPool* getPool() const {
+    return pool_;
   }
 
-  VariantData* getData() const {
-    return collectionToVariant(_data);
+  detail::VariantData* getData() const {
+    return detail::collectionToVariant(data_);
   }
 
-  VariantData* getOrCreateData() const {
-    return collectionToVariant(_data);
+  detail::VariantData* getOrCreateData() const {
+    return detail::collectionToVariant(data_);
   }
 
   template <typename TAdaptedString>
-  inline VariantData* getMember(TAdaptedString key) const {
-    if (!_data)
+  inline detail::VariantData* getMember(TAdaptedString key) const {
+    if (!data_)
       return 0;
-    return _data->getMember(key);
+    return data_->getMember(key);
   }
 
   template <typename TAdaptedString>
   void removeMember(TAdaptedString key) const {
-    if (!_data)
+    if (!data_)
       return;
-    _data->removeMember(key);
+    data_->removeMember(key);
   }
 
-  CollectionData* _data;
-  MemoryPool* _pool;
+  detail::CollectionData* data_;
+  detail::MemoryPool* pool_;
 };
 
 template <>
-struct Converter<JsonObject> : private VariantAttorney {
+struct Converter<JsonObject> : private detail::VariantAttorney {
   static void toJson(JsonVariantConst src, JsonVariant dst) {
     variantCopyFrom(getData(dst), getData(src), getPool(dst));
   }
 
   static JsonObject fromJson(JsonVariant src) {
-    VariantData* data = getData(src);
-    MemoryPool* pool = getPool(src);
+    auto data = getData(src);
+    auto pool = getPool(src);
     return JsonObject(pool, data != 0 ? data->asObject() : 0);
   }
 
-  static InvalidConversion<JsonVariantConst, JsonObject> fromJson(
+  static detail::InvalidConversion<JsonVariantConst, JsonObject> fromJson(
       JsonVariantConst);
 
   static bool checkJson(JsonVariantConst) {
@@ -239,8 +243,9 @@ struct Converter<JsonObject> : private VariantAttorney {
   }
 
   static bool checkJson(JsonVariant src) {
-    VariantData* data = getData(src);
+    auto data = getData(src);
     return data && data->isObject();
   }
 };
-}  // namespace ARDUINOJSON_NAMESPACE
+
+ARDUINOJSON_END_PUBLIC_NAMESPACE
diff --git a/neosensor/libraries/ArduinoJson/src/ArduinoJson/Object/JsonObjectConst.hpp b/neosensor/libraries/ArduinoJson/src/ArduinoJson/Object/JsonObjectConst.hpp
index 93ad9d86..e9b5e483 100644
--- a/neosensor/libraries/ArduinoJson/src/ArduinoJson/Object/JsonObjectConst.hpp
+++ b/neosensor/libraries/ArduinoJson/src/ArduinoJson/Object/JsonObjectConst.hpp
@@ -1,5 +1,5 @@
 // ArduinoJson - https://arduinojson.org
-// Copyright © 2014-2022, Benoit BLANCHON
+// Copyright © 2014-2023, Benoit BLANCHON
 // MIT License
 
 #pragma once
@@ -7,63 +7,63 @@
 #include <ArduinoJson/Object/JsonObjectIterator.hpp>
 #include <ArduinoJson/Variant/VariantOperators.hpp>
 
-namespace ARDUINOJSON_NAMESPACE {
+ARDUINOJSON_BEGIN_PUBLIC_NAMESPACE
 
 // A read-only reference to an object in a JsonDocument.
 // https://arduinojson.org/v6/api/jsonobjectconst/
-class JsonObjectConst : public VariantOperators<JsonObjectConst> {
+class JsonObjectConst : public detail::VariantOperators<JsonObjectConst> {
   friend class JsonObject;
-  friend class VariantAttorney;
+  friend class detail::VariantAttorney;
 
  public:
   typedef JsonObjectConstIterator iterator;
 
   // Creates an unbound reference.
-  JsonObjectConst() : _data(0) {}
+  JsonObjectConst() : data_(0) {}
 
   // INTERNAL USE ONLY
-  JsonObjectConst(const CollectionData* data) : _data(data) {}
+  JsonObjectConst(const detail::CollectionData* data) : data_(data) {}
 
   operator JsonVariantConst() const {
-    return JsonVariantConst(collectionToVariant(_data));
+    return JsonVariantConst(collectionToVariant(data_));
   }
 
   // Returns true if the reference is unbound.
   // https://arduinojson.org/v6/api/jsonobjectconst/isnull/
   FORCE_INLINE bool isNull() const {
-    return _data == 0;
+    return data_ == 0;
   }
 
   // Returns true if the reference is bound.
   // https://arduinojson.org/v6/api/jsonobjectconst/isnull/
   FORCE_INLINE operator bool() const {
-    return _data != 0;
+    return data_ != 0;
   }
 
   // Returns the number of bytes occupied by the object.
   // https://arduinojson.org/v6/api/jsonobjectconst/memoryusage/
   FORCE_INLINE size_t memoryUsage() const {
-    return _data ? _data->memoryUsage() : 0;
+    return data_ ? data_->memoryUsage() : 0;
   }
 
   // Returns the depth (nesting level) of the object.
   // https://arduinojson.org/v6/api/jsonobjectconst/nesting/
   FORCE_INLINE size_t nesting() const {
-    return variantNesting(collectionToVariant(_data));
+    return variantNesting(collectionToVariant(data_));
   }
 
   // Returns the number of members in the object.
   // https://arduinojson.org/v6/api/jsonobjectconst/size/
   FORCE_INLINE size_t size() const {
-    return _data ? _data->size() : 0;
+    return data_ ? data_->size() : 0;
   }
 
   // Returns an iterator to the first key-value pair of the object.
   // https://arduinojson.org/v6/api/jsonobjectconst/begin/
   FORCE_INLINE iterator begin() const {
-    if (!_data)
+    if (!data_)
       return iterator();
-    return iterator(_data->head());
+    return iterator(data_->head());
   }
 
   // Returns an iterator following the last key-value pair of the object.
@@ -76,40 +76,40 @@ class JsonObjectConst : public VariantOperators<JsonObjectConst> {
   // https://arduinojson.org/v6/api/jsonobjectconst/containskey/
   template <typename TString>
   FORCE_INLINE bool containsKey(const TString& key) const {
-    return getMember(adaptString(key)) != 0;
+    return getMember(detail::adaptString(key)) != 0;
   }
 
   // Returns true if the object contains the specified key.
   // https://arduinojson.org/v6/api/jsonobjectconst/containskey/
   template <typename TChar>
   FORCE_INLINE bool containsKey(TChar* key) const {
-    return getMember(adaptString(key)) != 0;
+    return getMember(detail::adaptString(key)) != 0;
   }
 
   // Gets the member with specified key.
   // https://arduinojson.org/v6/api/jsonobjectconst/subscript/
   template <typename TString>
-  FORCE_INLINE
-      typename enable_if<IsString<TString>::value, JsonVariantConst>::type
-      operator[](const TString& key) const {
-    return JsonVariantConst(getMember(adaptString(key)));
+  FORCE_INLINE typename detail::enable_if<detail::IsString<TString>::value,
+                                          JsonVariantConst>::type
+  operator[](const TString& key) const {
+    return JsonVariantConst(getMember(detail::adaptString(key)));
   }
 
   // Gets the member with specified key.
   // https://arduinojson.org/v6/api/jsonobjectconst/subscript/
   template <typename TChar>
-  FORCE_INLINE
-      typename enable_if<IsString<TChar*>::value, JsonVariantConst>::type
-      operator[](TChar* key) const {
-    return JsonVariantConst(getMember(adaptString(key)));
+  FORCE_INLINE typename detail::enable_if<detail::IsString<TChar*>::value,
+                                          JsonVariantConst>::type
+  operator[](TChar* key) const {
+    return JsonVariantConst(getMember(detail::adaptString(key)));
   }
 
   // Compares objects.
   FORCE_INLINE bool operator==(JsonObjectConst rhs) const {
-    if (_data == rhs._data)
+    if (data_ == rhs.data_)
       return true;
 
-    if (!_data || !rhs._data)
+    if (!data_ || !rhs.data_)
       return false;
 
     size_t count = 0;
@@ -122,35 +122,35 @@ class JsonObjectConst : public VariantOperators<JsonObjectConst> {
   }
 
  private:
-  const VariantData* getData() const {
-    return collectionToVariant(_data);
+  const detail::VariantData* getData() const {
+    return collectionToVariant(data_);
   }
 
   template <typename TAdaptedString>
-  const VariantData* getMember(TAdaptedString key) const {
-    if (!_data)
+  const detail::VariantData* getMember(TAdaptedString key) const {
+    if (!data_)
       return 0;
-    return _data->getMember(key);
+    return data_->getMember(key);
   }
 
-  const CollectionData* _data;
+  const detail::CollectionData* data_;
 };
 
 template <>
-struct Converter<JsonObjectConst> : private VariantAttorney {
+struct Converter<JsonObjectConst> : private detail::VariantAttorney {
   static void toJson(JsonVariantConst src, JsonVariant dst) {
     variantCopyFrom(getData(dst), getData(src), getPool(dst));
   }
 
   static JsonObjectConst fromJson(JsonVariantConst src) {
-    const VariantData* data = getData(src);
+    auto data = getData(src);
     return data != 0 ? data->asObject() : 0;
   }
 
   static bool checkJson(JsonVariantConst src) {
-    const VariantData* data = getData(src);
+    auto data = getData(src);
     return data && data->isObject();
   }
 };
 
-}  // namespace ARDUINOJSON_NAMESPACE
+ARDUINOJSON_END_PUBLIC_NAMESPACE
diff --git a/neosensor/libraries/ArduinoJson/src/ArduinoJson/Object/JsonObjectImpl.hpp b/neosensor/libraries/ArduinoJson/src/ArduinoJson/Object/JsonObjectImpl.hpp
index fc6c96e2..3b24081a 100644
--- a/neosensor/libraries/ArduinoJson/src/ArduinoJson/Object/JsonObjectImpl.hpp
+++ b/neosensor/libraries/ArduinoJson/src/ArduinoJson/Object/JsonObjectImpl.hpp
@@ -1,5 +1,5 @@
 // ArduinoJson - https://arduinojson.org
-// Copyright © 2014-2022, Benoit BLANCHON
+// Copyright © 2014-2023, Benoit BLANCHON
 // MIT License
 
 #pragma once
@@ -7,7 +7,7 @@
 #include <ArduinoJson/Array/JsonArray.hpp>
 #include <ArduinoJson/Object/JsonObject.hpp>
 
-namespace ARDUINOJSON_NAMESPACE {
+ARDUINOJSON_BEGIN_PUBLIC_NAMESPACE
 
 template <typename TString>
 inline JsonArray JsonObject::createNestedArray(const TString& key) const {
@@ -19,6 +19,10 @@ inline JsonArray JsonObject::createNestedArray(TChar* key) const {
   return operator[](key).template to<JsonArray>();
 }
 
+ARDUINOJSON_END_PUBLIC_NAMESPACE
+
+ARDUINOJSON_BEGIN_PRIVATE_NAMESPACE
+
 template <typename TDerived>
 template <typename TString>
 inline JsonArray VariantRefBase<TDerived>::createNestedArray(
@@ -65,7 +69,7 @@ VariantRefBase<TDerived>::containsKey(TChar* key) const {
 template <typename TDerived>
 template <typename TString>
 inline typename enable_if<IsString<TString*>::value,
-                          MemberProxy<TDerived, TString*> >::type
+                          MemberProxy<TDerived, TString*>>::type
 VariantRefBase<TDerived>::operator[](TString* key) const {
   return MemberProxy<TDerived, TString*>(derived(), key);
 }
@@ -73,9 +77,9 @@ VariantRefBase<TDerived>::operator[](TString* key) const {
 template <typename TDerived>
 template <typename TString>
 inline typename enable_if<IsString<TString>::value,
-                          MemberProxy<TDerived, TString> >::type
+                          MemberProxy<TDerived, TString>>::type
 VariantRefBase<TDerived>::operator[](const TString& key) const {
   return MemberProxy<TDerived, TString>(derived(), key);
 }
 
-}  // namespace ARDUINOJSON_NAMESPACE
+ARDUINOJSON_END_PRIVATE_NAMESPACE
diff --git a/neosensor/libraries/ArduinoJson/src/ArduinoJson/Object/JsonObjectIterator.hpp b/neosensor/libraries/ArduinoJson/src/ArduinoJson/Object/JsonObjectIterator.hpp
index b01babb0..55c09ed7 100644
--- a/neosensor/libraries/ArduinoJson/src/ArduinoJson/Object/JsonObjectIterator.hpp
+++ b/neosensor/libraries/ArduinoJson/src/ArduinoJson/Object/JsonObjectIterator.hpp
@@ -1,5 +1,5 @@
 // ArduinoJson - https://arduinojson.org
-// Copyright © 2014-2022, Benoit BLANCHON
+// Copyright © 2014-2023, Benoit BLANCHON
 // MIT License
 
 #pragma once
@@ -7,113 +7,117 @@
 #include <ArduinoJson/Object/JsonPair.hpp>
 #include <ArduinoJson/Variant/SlotFunctions.hpp>
 
-namespace ARDUINOJSON_NAMESPACE {
+ARDUINOJSON_BEGIN_PUBLIC_NAMESPACE
 
 class JsonPairPtr {
  public:
-  JsonPairPtr(MemoryPool* pool, VariantSlot* slot) : _pair(pool, slot) {}
+  JsonPairPtr(detail::MemoryPool* pool, detail::VariantSlot* slot)
+      : pair_(pool, slot) {}
 
   const JsonPair* operator->() const {
-    return &_pair;
+    return &pair_;
   }
 
   const JsonPair& operator*() const {
-    return _pair;
+    return pair_;
   }
 
  private:
-  JsonPair _pair;
+  JsonPair pair_;
 };
 
 class JsonObjectIterator {
   friend class JsonObject;
 
  public:
-  JsonObjectIterator() : _slot(0) {}
+  JsonObjectIterator() : slot_(0) {}
 
-  explicit JsonObjectIterator(MemoryPool* pool, VariantSlot* slot)
-      : _pool(pool), _slot(slot) {}
+  explicit JsonObjectIterator(detail::MemoryPool* pool,
+                              detail::VariantSlot* slot)
+      : pool_(pool), slot_(slot) {}
 
   JsonPair operator*() const {
-    return JsonPair(_pool, _slot);
+    return JsonPair(pool_, slot_);
   }
   JsonPairPtr operator->() {
-    return JsonPairPtr(_pool, _slot);
+    return JsonPairPtr(pool_, slot_);
   }
 
   bool operator==(const JsonObjectIterator& other) const {
-    return _slot == other._slot;
+    return slot_ == other.slot_;
   }
 
   bool operator!=(const JsonObjectIterator& other) const {
-    return _slot != other._slot;
+    return slot_ != other.slot_;
   }
 
   JsonObjectIterator& operator++() {
-    _slot = _slot->next();
+    slot_ = slot_->next();
     return *this;
   }
 
   JsonObjectIterator& operator+=(size_t distance) {
-    _slot = _slot->next(distance);
+    slot_ = slot_->next(distance);
     return *this;
   }
 
  private:
-  MemoryPool* _pool;
-  VariantSlot* _slot;
+  detail::MemoryPool* pool_;
+  detail::VariantSlot* slot_;
 };
 
 class JsonPairConstPtr {
  public:
-  JsonPairConstPtr(const VariantSlot* slot) : _pair(slot) {}
+  JsonPairConstPtr(const detail::VariantSlot* slot) : pair_(slot) {}
 
   const JsonPairConst* operator->() const {
-    return &_pair;
+    return &pair_;
   }
 
   const JsonPairConst& operator*() const {
-    return _pair;
+    return pair_;
   }
 
  private:
-  JsonPairConst _pair;
+  JsonPairConst pair_;
 };
 
 class JsonObjectConstIterator {
   friend class JsonObject;
 
  public:
-  JsonObjectConstIterator() : _slot(0) {}
+  JsonObjectConstIterator() : slot_(0) {}
 
-  explicit JsonObjectConstIterator(const VariantSlot* slot) : _slot(slot) {}
+  explicit JsonObjectConstIterator(const detail::VariantSlot* slot)
+      : slot_(slot) {}
 
   JsonPairConst operator*() const {
-    return JsonPairConst(_slot);
+    return JsonPairConst(slot_);
   }
   JsonPairConstPtr operator->() {
-    return JsonPairConstPtr(_slot);
+    return JsonPairConstPtr(slot_);
   }
 
   bool operator==(const JsonObjectConstIterator& other) const {
-    return _slot == other._slot;
+    return slot_ == other.slot_;
   }
 
   bool operator!=(const JsonObjectConstIterator& other) const {
-    return _slot != other._slot;
+    return slot_ != other.slot_;
   }
 
   JsonObjectConstIterator& operator++() {
-    _slot = _slot->next();
+    slot_ = slot_->next();
     return *this;
   }
 
   JsonObjectConstIterator& operator+=(size_t distance) {
-    _slot = _slot->next(distance);
+    slot_ = slot_->next(distance);
     return *this;
   }
 
  private:
-  const VariantSlot* _slot;
+  const detail::VariantSlot* slot_;
 };
-}  // namespace ARDUINOJSON_NAMESPACE
+
+ARDUINOJSON_END_PUBLIC_NAMESPACE
diff --git a/neosensor/libraries/ArduinoJson/src/ArduinoJson/Object/JsonPair.hpp b/neosensor/libraries/ArduinoJson/src/ArduinoJson/Object/JsonPair.hpp
index fa86a7c6..862a6579 100644
--- a/neosensor/libraries/ArduinoJson/src/ArduinoJson/Object/JsonPair.hpp
+++ b/neosensor/libraries/ArduinoJson/src/ArduinoJson/Object/JsonPair.hpp
@@ -1,5 +1,5 @@
 // ArduinoJson - https://arduinojson.org
-// Copyright © 2014-2022, Benoit BLANCHON
+// Copyright © 2014-2023, Benoit BLANCHON
 // MIT License
 
 #pragma once
@@ -8,60 +8,61 @@
 #include <ArduinoJson/Variant/JsonVariant.hpp>
 #include <ArduinoJson/Variant/JsonVariantConst.hpp>
 
-namespace ARDUINOJSON_NAMESPACE {
+ARDUINOJSON_BEGIN_PUBLIC_NAMESPACE
 
 // A key-value pair.
 // https://arduinojson.org/v6/api/jsonobject/begin_end/
 class JsonPair {
  public:
   // INTERNAL USE ONLY
-  JsonPair(MemoryPool* pool, VariantSlot* slot) {
+  JsonPair(detail::MemoryPool* pool, detail::VariantSlot* slot) {
     if (slot) {
-      _key = JsonString(slot->key(), slot->ownsKey() ? JsonString::Copied
+      key_ = JsonString(slot->key(), slot->ownsKey() ? JsonString::Copied
                                                      : JsonString::Linked);
-      _value = JsonVariant(pool, slot->data());
+      value_ = JsonVariant(pool, slot->data());
     }
   }
 
   // Returns the key.
   JsonString key() const {
-    return _key;
+    return key_;
   }
 
   // Returns the value.
   JsonVariant value() const {
-    return _value;
+    return value_;
   }
 
  private:
-  JsonString _key;
-  JsonVariant _value;
+  JsonString key_;
+  JsonVariant value_;
 };
 
 // A read-only key-value pair.
 // https://arduinojson.org/v6/api/jsonobjectconst/begin_end/
 class JsonPairConst {
  public:
-  JsonPairConst(const VariantSlot* slot) {
+  JsonPairConst(const detail::VariantSlot* slot) {
     if (slot) {
-      _key = JsonString(slot->key(), slot->ownsKey() ? JsonString::Copied
+      key_ = JsonString(slot->key(), slot->ownsKey() ? JsonString::Copied
                                                      : JsonString::Linked);
-      _value = JsonVariantConst(slot->data());
+      value_ = JsonVariantConst(slot->data());
     }
   }
 
   // Returns the key.
   JsonString key() const {
-    return _key;
+    return key_;
   }
 
   // Returns the value.
   JsonVariantConst value() const {
-    return _value;
+    return value_;
   }
 
  private:
-  JsonString _key;
-  JsonVariantConst _value;
+  JsonString key_;
+  JsonVariantConst value_;
 };
-}  // namespace ARDUINOJSON_NAMESPACE
+
+ARDUINOJSON_END_PUBLIC_NAMESPACE
diff --git a/neosensor/libraries/ArduinoJson/src/ArduinoJson/Object/MemberProxy.hpp b/neosensor/libraries/ArduinoJson/src/ArduinoJson/Object/MemberProxy.hpp
index 4720600e..78e5a521 100644
--- a/neosensor/libraries/ArduinoJson/src/ArduinoJson/Object/MemberProxy.hpp
+++ b/neosensor/libraries/ArduinoJson/src/ArduinoJson/Object/MemberProxy.hpp
@@ -1,27 +1,27 @@
 // ArduinoJson - https://arduinojson.org
-// Copyright © 2014-2022, Benoit BLANCHON
+// Copyright © 2014-2023, Benoit BLANCHON
 // MIT License
 
 #pragma once
 
 #include <ArduinoJson/Variant/VariantRefBase.hpp>
 
-namespace ARDUINOJSON_NAMESPACE {
+ARDUINOJSON_BEGIN_PRIVATE_NAMESPACE
 
 // A proxy class to get or set a member of an object.
 // https://arduinojson.org/v6/api/jsonobject/subscript/
 template <typename TUpstream, typename TStringRef>
 class MemberProxy
-    : public VariantRefBase<MemberProxy<TUpstream, TStringRef> >,
-      public VariantOperators<MemberProxy<TUpstream, TStringRef> > {
+    : public VariantRefBase<MemberProxy<TUpstream, TStringRef>>,
+      public VariantOperators<MemberProxy<TUpstream, TStringRef>> {
   friend class VariantAttorney;
 
  public:
   FORCE_INLINE MemberProxy(TUpstream upstream, TStringRef key)
-      : _upstream(upstream), _key(key) {}
+      : upstream_(upstream), key_(key) {}
 
   MemberProxy(const MemberProxy& src)
-      : _upstream(src._upstream), _key(src._key) {}
+      : upstream_(src.upstream_), key_(src.key_) {}
 
   FORCE_INLINE MemberProxy& operator=(const MemberProxy& src) {
     this->set(src);
@@ -42,23 +42,23 @@ class MemberProxy
 
  private:
   FORCE_INLINE MemoryPool* getPool() const {
-    return VariantAttorney::getPool(_upstream);
+    return VariantAttorney::getPool(upstream_);
   }
 
   FORCE_INLINE VariantData* getData() const {
-    return variantGetMember(VariantAttorney::getData(_upstream),
-                            adaptString(_key));
+    return variantGetMember(VariantAttorney::getData(upstream_),
+                            adaptString(key_));
   }
 
   FORCE_INLINE VariantData* getOrCreateData() const {
-    return variantGetOrAddMember(VariantAttorney::getOrCreateData(_upstream),
-                                 adaptString(_key),
-                                 VariantAttorney::getPool(_upstream));
+    return variantGetOrAddMember(VariantAttorney::getOrCreateData(upstream_),
+                                 adaptString(key_),
+                                 VariantAttorney::getPool(upstream_));
   }
 
  private:
-  TUpstream _upstream;
-  TStringRef _key;
+  TUpstream upstream_;
+  TStringRef key_;
 };
 
-}  // namespace ARDUINOJSON_NAMESPACE
+ARDUINOJSON_END_PRIVATE_NAMESPACE
diff --git a/neosensor/libraries/ArduinoJson/src/ArduinoJson/Polyfills/alias_cast.hpp b/neosensor/libraries/ArduinoJson/src/ArduinoJson/Polyfills/alias_cast.hpp
index 7030b7d9..4dac6c46 100644
--- a/neosensor/libraries/ArduinoJson/src/ArduinoJson/Polyfills/alias_cast.hpp
+++ b/neosensor/libraries/ArduinoJson/src/ArduinoJson/Polyfills/alias_cast.hpp
@@ -1,5 +1,5 @@
 // ArduinoJson - https://arduinojson.org
-// Copyright © 2014-2022, Benoit BLANCHON
+// Copyright © 2014-2023, Benoit BLANCHON
 // MIT License
 
 #pragma once
@@ -10,7 +10,7 @@
 #include <ArduinoJson/Configuration.hpp>
 #include "math.hpp"
 
-namespace ARDUINOJSON_NAMESPACE {
+ARDUINOJSON_BEGIN_PRIVATE_NAMESPACE
 
 template <typename T, typename F>
 struct alias_cast_t {
@@ -26,4 +26,5 @@ T alias_cast(F raw_data) {
   ac.raw = raw_data;
   return ac.data;
 }
-}  // namespace ARDUINOJSON_NAMESPACE
+
+ARDUINOJSON_END_PRIVATE_NAMESPACE
diff --git a/neosensor/libraries/ArduinoJson/src/ArduinoJson/Polyfills/assert.hpp b/neosensor/libraries/ArduinoJson/src/ArduinoJson/Polyfills/assert.hpp
index 9b17ab45..819bcf50 100644
--- a/neosensor/libraries/ArduinoJson/src/ArduinoJson/Polyfills/assert.hpp
+++ b/neosensor/libraries/ArduinoJson/src/ArduinoJson/Polyfills/assert.hpp
@@ -1,5 +1,5 @@
 // ArduinoJson - https://arduinojson.org
-// Copyright © 2014-2022, Benoit BLANCHON
+// Copyright © 2014-2023, Benoit BLANCHON
 // MIT License
 
 #pragma once
diff --git a/neosensor/libraries/ArduinoJson/src/ArduinoJson/Polyfills/attributes.hpp b/neosensor/libraries/ArduinoJson/src/ArduinoJson/Polyfills/attributes.hpp
index 9e37c82a..c4196fbb 100644
--- a/neosensor/libraries/ArduinoJson/src/ArduinoJson/Polyfills/attributes.hpp
+++ b/neosensor/libraries/ArduinoJson/src/ArduinoJson/Polyfills/attributes.hpp
@@ -1,5 +1,5 @@
 // ArduinoJson - https://arduinojson.org
-// Copyright © 2014-2022, Benoit BLANCHON
+// Copyright © 2014-2023, Benoit BLANCHON
 // MIT License
 
 #pragma once
@@ -21,12 +21,6 @@
 
 #endif
 
-#if __cplusplus >= 201103L
-#  define NOEXCEPT noexcept
-#else
-#  define NOEXCEPT throw()
-#endif
-
 #if defined(__has_attribute)
 #  if __has_attribute(no_sanitize)
 #    define ARDUINOJSON_NO_SANITIZE(check) __attribute__((no_sanitize(check)))
diff --git a/neosensor/libraries/ArduinoJson/src/ArduinoJson/Polyfills/ctype.hpp b/neosensor/libraries/ArduinoJson/src/ArduinoJson/Polyfills/ctype.hpp
index 7a957b95..b3a6231c 100644
--- a/neosensor/libraries/ArduinoJson/src/ArduinoJson/Polyfills/ctype.hpp
+++ b/neosensor/libraries/ArduinoJson/src/ArduinoJson/Polyfills/ctype.hpp
@@ -1,12 +1,12 @@
 // ArduinoJson - https://arduinojson.org
-// Copyright © 2014-2022, Benoit BLANCHON
+// Copyright © 2014-2023, Benoit BLANCHON
 // MIT License
 
 #pragma once
 
 #include <ArduinoJson/Namespace.hpp>
 
-namespace ARDUINOJSON_NAMESPACE {
+ARDUINOJSON_BEGIN_PRIVATE_NAMESPACE
 
 #ifndef isdigit
 inline bool isdigit(char c) {
@@ -17,4 +17,5 @@ inline bool isdigit(char c) {
 inline bool issign(char c) {
   return '-' == c || c == '+';
 }
-}  // namespace ARDUINOJSON_NAMESPACE
+
+ARDUINOJSON_END_PRIVATE_NAMESPACE
diff --git a/neosensor/libraries/ArduinoJson/src/ArduinoJson/Polyfills/integer.hpp b/neosensor/libraries/ArduinoJson/src/ArduinoJson/Polyfills/integer.hpp
index 08708d57..2bfce365 100644
--- a/neosensor/libraries/ArduinoJson/src/ArduinoJson/Polyfills/integer.hpp
+++ b/neosensor/libraries/ArduinoJson/src/ArduinoJson/Polyfills/integer.hpp
@@ -1,5 +1,5 @@
 // ArduinoJson - https://arduinojson.org
-// Copyright © 2014-2022, Benoit BLANCHON
+// Copyright © 2014-2023, Benoit BLANCHON
 // MIT License
 
 #pragma once
@@ -8,7 +8,7 @@
 
 #include <ArduinoJson/Namespace.hpp>
 
-namespace ARDUINOJSON_NAMESPACE {
+ARDUINOJSON_BEGIN_PRIVATE_NAMESPACE
 
 template <int Bits>
 struct int_t;
@@ -27,4 +27,5 @@ template <>
 struct int_t<32> {
   typedef int32_t type;
 };
-}  // namespace ARDUINOJSON_NAMESPACE
+
+ARDUINOJSON_END_PRIVATE_NAMESPACE
diff --git a/neosensor/libraries/ArduinoJson/src/ArduinoJson/Polyfills/limits.hpp b/neosensor/libraries/ArduinoJson/src/ArduinoJson/Polyfills/limits.hpp
index 9c26b67a..cf799da6 100644
--- a/neosensor/libraries/ArduinoJson/src/ArduinoJson/Polyfills/limits.hpp
+++ b/neosensor/libraries/ArduinoJson/src/ArduinoJson/Polyfills/limits.hpp
@@ -1,5 +1,5 @@
 // ArduinoJson - https://arduinojson.org
-// Copyright © 2014-2022, Benoit BLANCHON
+// Copyright © 2014-2023, Benoit BLANCHON
 // MIT License
 
 #pragma once
@@ -11,7 +11,7 @@
 #  pragma warning(disable : 4310)
 #endif
 
-namespace ARDUINOJSON_NAMESPACE {
+ARDUINOJSON_BEGIN_PRIVATE_NAMESPACE
 
 // Differs from standard because we can't use the symbols "min" and "max"
 template <typename T, typename Enable = void>
@@ -38,7 +38,7 @@ struct numeric_limits<
   }
 };
 
-}  // namespace ARDUINOJSON_NAMESPACE
+ARDUINOJSON_END_PRIVATE_NAMESPACE
 
 #ifdef _MSC_VER
 #  pragma warning(pop)
diff --git a/neosensor/libraries/ArduinoJson/src/ArduinoJson/Polyfills/math.hpp b/neosensor/libraries/ArduinoJson/src/ArduinoJson/Polyfills/math.hpp
index 2b77c5c5..45fc8d2f 100644
--- a/neosensor/libraries/ArduinoJson/src/ArduinoJson/Polyfills/math.hpp
+++ b/neosensor/libraries/ArduinoJson/src/ArduinoJson/Polyfills/math.hpp
@@ -1,12 +1,12 @@
 // ArduinoJson - https://arduinojson.org
-// Copyright © 2014-2022, Benoit BLANCHON
+// Copyright © 2014-2023, Benoit BLANCHON
 // MIT License
 
 #pragma once
 
 #include <ArduinoJson/Namespace.hpp>
 
-namespace ARDUINOJSON_NAMESPACE {
+ARDUINOJSON_BEGIN_PRIVATE_NAMESPACE
 
 // Some libraries #define isnan() and isinf() so we need to check before
 // using this name
@@ -24,4 +24,4 @@ bool isinf(T x) {
   return x != 0.0 && x * 2 == x;
 }
 #endif
-}  // namespace ARDUINOJSON_NAMESPACE
+ARDUINOJSON_END_PRIVATE_NAMESPACE
diff --git a/neosensor/libraries/ArduinoJson/src/ArduinoJson/Polyfills/mpl/max.hpp b/neosensor/libraries/ArduinoJson/src/ArduinoJson/Polyfills/mpl/max.hpp
index e3b09119..3970b838 100644
--- a/neosensor/libraries/ArduinoJson/src/ArduinoJson/Polyfills/mpl/max.hpp
+++ b/neosensor/libraries/ArduinoJson/src/ArduinoJson/Polyfills/mpl/max.hpp
@@ -1,5 +1,5 @@
 // ArduinoJson - https://arduinojson.org
-// Copyright © 2014-2022, Benoit BLANCHON
+// Copyright © 2014-2023, Benoit BLANCHON
 // MIT License
 
 #pragma once
@@ -8,7 +8,7 @@
 
 #include <stddef.h>  // for size_t
 
-namespace ARDUINOJSON_NAMESPACE {
+ARDUINOJSON_BEGIN_PRIVATE_NAMESPACE
 
 // A meta-function that returns the highest value
 template <size_t X, size_t Y, bool MaxIsX = (X > Y)>
@@ -23,4 +23,5 @@ template <size_t X, size_t Y>
 struct Max<X, Y, false> {
   static const size_t value = Y;
 };
-}  // namespace ARDUINOJSON_NAMESPACE
+
+ARDUINOJSON_END_PRIVATE_NAMESPACE
diff --git a/neosensor/libraries/ArduinoJson/src/ArduinoJson/Polyfills/pgmspace.hpp b/neosensor/libraries/ArduinoJson/src/ArduinoJson/Polyfills/pgmspace.hpp
index 3e39a37e..9e66f96a 100644
--- a/neosensor/libraries/ArduinoJson/src/ArduinoJson/Polyfills/pgmspace.hpp
+++ b/neosensor/libraries/ArduinoJson/src/ArduinoJson/Polyfills/pgmspace.hpp
@@ -1,26 +1,33 @@
 // ArduinoJson - https://arduinojson.org
-// Copyright © 2014-2022, Benoit BLANCHON
+// Copyright © 2014-2023, Benoit BLANCHON
 // MIT License
 
 #pragma once
 
-#include <Arduino.h>
+#ifdef ARDUINO
+#  include <Arduino.h>
+#else
+// Allow using PROGMEM outside of Arduino (issue #1903)
+class __FlashStringHelper;
+#  include <avr/pgmspace.h>
+#endif
 
 #include <ArduinoJson/Configuration.hpp>
 #include <ArduinoJson/Namespace.hpp>
 #include <ArduinoJson/Polyfills/assert.hpp>
 
-namespace ARDUINOJSON_NAMESPACE {
+ARDUINOJSON_BEGIN_PRIVATE_NAMESPACE
 // Wraps a const char* so that the our functions are picked only if the
 // originals are missing
 struct pgm_p {
   pgm_p(const void* p) : address(reinterpret_cast<const char*>(p)) {}
   const char* address;
 };
-}  // namespace ARDUINOJSON_NAMESPACE
+
+ARDUINOJSON_END_PRIVATE_NAMESPACE
 
 #ifndef strlen_P
-inline size_t strlen_P(ARDUINOJSON_NAMESPACE::pgm_p s) {
+inline size_t strlen_P(ArduinoJson::detail::pgm_p s) {
   const char* p = s.address;
   ARDUINOJSON_ASSERT(p != NULL);
   while (pgm_read_byte(p))
@@ -30,7 +37,7 @@ inline size_t strlen_P(ARDUINOJSON_NAMESPACE::pgm_p s) {
 #endif
 
 #ifndef strncmp_P
-inline int strncmp_P(const char* a, ARDUINOJSON_NAMESPACE::pgm_p b, size_t n) {
+inline int strncmp_P(const char* a, ArduinoJson::detail::pgm_p b, size_t n) {
   const char* s1 = a;
   const char* s2 = b.address;
   ARDUINOJSON_ASSERT(s1 != NULL);
@@ -50,7 +57,7 @@ inline int strncmp_P(const char* a, ARDUINOJSON_NAMESPACE::pgm_p b, size_t n) {
 #endif
 
 #ifndef strcmp_P
-inline int strcmp_P(const char* a, ARDUINOJSON_NAMESPACE::pgm_p b) {
+inline int strcmp_P(const char* a, ArduinoJson::detail::pgm_p b) {
   const char* s1 = a;
   const char* s2 = b.address;
   ARDUINOJSON_ASSERT(s1 != NULL);
@@ -69,7 +76,7 @@ inline int strcmp_P(const char* a, ARDUINOJSON_NAMESPACE::pgm_p b) {
 #endif
 
 #ifndef memcmp_P
-inline int memcmp_P(const void* a, ARDUINOJSON_NAMESPACE::pgm_p b, size_t n) {
+inline int memcmp_P(const void* a, ArduinoJson::detail::pgm_p b, size_t n) {
   const uint8_t* p1 = reinterpret_cast<const uint8_t*>(a);
   const char* p2 = b.address;
   ARDUINOJSON_ASSERT(p1 != NULL);
@@ -85,7 +92,7 @@ inline int memcmp_P(const void* a, ARDUINOJSON_NAMESPACE::pgm_p b, size_t n) {
 #endif
 
 #ifndef memcpy_P
-inline void* memcpy_P(void* dst, ARDUINOJSON_NAMESPACE::pgm_p src, size_t n) {
+inline void* memcpy_P(void* dst, ArduinoJson::detail::pgm_p src, size_t n) {
   uint8_t* d = reinterpret_cast<uint8_t*>(dst);
   const char* s = src.address;
   ARDUINOJSON_ASSERT(d != NULL);
@@ -98,15 +105,38 @@ inline void* memcpy_P(void* dst, ARDUINOJSON_NAMESPACE::pgm_p src, size_t n) {
 #endif
 
 #ifndef pgm_read_dword
-inline uint32_t pgm_read_dword(ARDUINOJSON_NAMESPACE::pgm_p p) {
+inline uint32_t pgm_read_dword(ArduinoJson::detail::pgm_p p) {
   uint32_t result;
   memcpy_P(&result, p.address, 4);
   return result;
 }
 #endif
 
+#ifndef pgm_read_float
+inline float pgm_read_float(ArduinoJson::detail::pgm_p p) {
+  float result;
+  memcpy_P(&result, p.address, sizeof(float));
+  return result;
+}
+#endif
+
+#ifndef pgm_read_double
+#  if defined(__SIZEOF_DOUBLE__) && defined(__SIZEOF_FLOAT__) && \
+      __SIZEOF_DOUBLE__ == __SIZEOF_FLOAT__
+inline double pgm_read_double(ArduinoJson::detail::pgm_p p) {
+  return pgm_read_float(p.address);
+}
+#  else
+inline double pgm_read_double(ArduinoJson::detail::pgm_p p) {
+  double result;
+  memcpy_P(&result, p.address, sizeof(double));
+  return result;
+}
+#  endif
+#endif
+
 #ifndef pgm_read_ptr
-inline void* pgm_read_ptr(ARDUINOJSON_NAMESPACE::pgm_p p) {
+inline void* pgm_read_ptr(ArduinoJson::detail::pgm_p p) {
   void* result;
   memcpy_P(&result, p.address, sizeof(result));
   return result;
diff --git a/neosensor/libraries/ArduinoJson/src/ArduinoJson/Polyfills/pgmspace_generic.hpp b/neosensor/libraries/ArduinoJson/src/ArduinoJson/Polyfills/pgmspace_generic.hpp
index d9e964b8..dc47581f 100644
--- a/neosensor/libraries/ArduinoJson/src/ArduinoJson/Polyfills/pgmspace_generic.hpp
+++ b/neosensor/libraries/ArduinoJson/src/ArduinoJson/Polyfills/pgmspace_generic.hpp
@@ -1,24 +1,67 @@
 // ArduinoJson - https://arduinojson.org
-// Copyright © 2014-2022, Benoit BLANCHON
+// Copyright © 2014-2023, Benoit BLANCHON
 // MIT License
 
 #pragma once
 
 #include <ArduinoJson/Namespace.hpp>
-#include <ArduinoJson/Polyfills/pgmspace.hpp>
-#include <ArduinoJson/Polyfills/type_traits.hpp>
 
-namespace ARDUINOJSON_NAMESPACE {
+#if ARDUINOJSON_ENABLE_PROGMEM
+#  include <ArduinoJson/Polyfills/pgmspace.hpp>
+#  include <ArduinoJson/Polyfills/type_traits.hpp>
+#endif
+
+ARDUINOJSON_BEGIN_PRIVATE_NAMESPACE
+
+#if ARDUINOJSON_ENABLE_PROGMEM
+
+#  ifndef ARDUINOJSON_DEFINE_PROGMEM_ARRAY
+#    define ARDUINOJSON_DEFINE_PROGMEM_ARRAY(type, name, ...) \
+      static type const name[] PROGMEM = __VA_ARGS__;
+#  endif
 
 template <typename T>
-typename enable_if<is_pointer<T>::value, T>::type pgm_read(const void* p) {
-  return reinterpret_cast<T>(pgm_read_ptr(p));
+inline const T* pgm_read(const T* const* p) {
+  return reinterpret_cast<const T*>(pgm_read_ptr(p));
 }
 
-template <typename T>
-typename enable_if<is_same<T, uint32_t>::value, T>::type pgm_read(
-    const void* p) {
+inline uint32_t pgm_read(const uint32_t* p) {
   return pgm_read_dword(p);
 }
 
-}  // namespace ARDUINOJSON_NAMESPACE
+inline double pgm_read(const double* p) {
+  return pgm_read_double(p);
+}
+
+inline float pgm_read(const float* p) {
+  return pgm_read_float(p);
+}
+
+#else
+
+#  ifndef ARDUINOJSON_DEFINE_PROGMEM_ARRAY
+#    define ARDUINOJSON_DEFINE_PROGMEM_ARRAY(type, name, ...) \
+      static type const name[] = __VA_ARGS__;
+#  endif
+
+template <typename T>
+inline T pgm_read(const T* p) {
+  return *p;
+}
+
+#endif
+
+template <typename T>
+class pgm_ptr {
+ public:
+  explicit pgm_ptr(const T* ptr) : ptr_(ptr) {}
+
+  T operator[](intptr_t index) const {
+    return pgm_read(ptr_ + index);
+  }
+
+ private:
+  const T* ptr_;
+};
+
+ARDUINOJSON_END_PRIVATE_NAMESPACE
diff --git a/neosensor/libraries/ArduinoJson/src/ArduinoJson/Polyfills/preprocessor.hpp b/neosensor/libraries/ArduinoJson/src/ArduinoJson/Polyfills/preprocessor.hpp
index c9b97c94..b91baf74 100644
--- a/neosensor/libraries/ArduinoJson/src/ArduinoJson/Polyfills/preprocessor.hpp
+++ b/neosensor/libraries/ArduinoJson/src/ArduinoJson/Polyfills/preprocessor.hpp
@@ -1,35 +1,31 @@
 // ArduinoJson - https://arduinojson.org
-// Copyright © 2014-2022, Benoit BLANCHON
+// Copyright © 2014-2023, Benoit BLANCHON
 // MIT License
 
 #pragma once
 
-#define ARDUINOJSON_EXPAND6(a, b, c, d, e, f) a, b, c, d, e, f
-#define ARDUINOJSON_EXPAND9(a, b, c, d, e, f, g, h, i) a, b, c, d, e, f, g, h, i
-#define ARDUINOJSON_EXPAND18(a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p, \
-                             q, r)                                           \
-  a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p, q, r
-
 #define ARDUINOJSON_CONCAT_(A, B) A##B
 #define ARDUINOJSON_CONCAT2(A, B) ARDUINOJSON_CONCAT_(A, B)
+#define ARDUINOJSON_CONCAT3(A, B, C) \
+  ARDUINOJSON_CONCAT2(ARDUINOJSON_CONCAT2(A, B), C)
 #define ARDUINOJSON_CONCAT4(A, B, C, D) \
   ARDUINOJSON_CONCAT2(ARDUINOJSON_CONCAT2(A, B), ARDUINOJSON_CONCAT2(C, D))
 
-#define ARDUINOJSON_HEX_DIGIT_0000() 0
-#define ARDUINOJSON_HEX_DIGIT_0001() 1
-#define ARDUINOJSON_HEX_DIGIT_0010() 2
-#define ARDUINOJSON_HEX_DIGIT_0011() 3
-#define ARDUINOJSON_HEX_DIGIT_0100() 4
-#define ARDUINOJSON_HEX_DIGIT_0101() 5
-#define ARDUINOJSON_HEX_DIGIT_0110() 6
-#define ARDUINOJSON_HEX_DIGIT_0111() 7
-#define ARDUINOJSON_HEX_DIGIT_1000() 8
-#define ARDUINOJSON_HEX_DIGIT_1001() 9
-#define ARDUINOJSON_HEX_DIGIT_1010() A
-#define ARDUINOJSON_HEX_DIGIT_1011() B
-#define ARDUINOJSON_HEX_DIGIT_1100() C
-#define ARDUINOJSON_HEX_DIGIT_1101() D
-#define ARDUINOJSON_HEX_DIGIT_1110() E
-#define ARDUINOJSON_HEX_DIGIT_1111() F
-#define ARDUINOJSON_HEX_DIGIT_(A, B, C, D) ARDUINOJSON_HEX_DIGIT_##A##B##C##D()
-#define ARDUINOJSON_HEX_DIGIT(A, B, C, D) ARDUINOJSON_HEX_DIGIT_(A, B, C, D)
+#define ARDUINOJSON_BIN2ALPHA_0000() A
+#define ARDUINOJSON_BIN2ALPHA_0001() B
+#define ARDUINOJSON_BIN2ALPHA_0010() C
+#define ARDUINOJSON_BIN2ALPHA_0011() D
+#define ARDUINOJSON_BIN2ALPHA_0100() E
+#define ARDUINOJSON_BIN2ALPHA_0101() F
+#define ARDUINOJSON_BIN2ALPHA_0110() F
+#define ARDUINOJSON_BIN2ALPHA_0111() H
+#define ARDUINOJSON_BIN2ALPHA_1000() I
+#define ARDUINOJSON_BIN2ALPHA_1001() J
+#define ARDUINOJSON_BIN2ALPHA_1010() K
+#define ARDUINOJSON_BIN2ALPHA_1011() L
+#define ARDUINOJSON_BIN2ALPHA_1100() M
+#define ARDUINOJSON_BIN2ALPHA_1101() N
+#define ARDUINOJSON_BIN2ALPHA_1110() O
+#define ARDUINOJSON_BIN2ALPHA_1111() P
+#define ARDUINOJSON_BIN2ALPHA_(A, B, C, D) ARDUINOJSON_BIN2ALPHA_##A##B##C##D()
+#define ARDUINOJSON_BIN2ALPHA(A, B, C, D) ARDUINOJSON_BIN2ALPHA_(A, B, C, D)
diff --git a/neosensor/libraries/ArduinoJson/src/ArduinoJson/Polyfills/static_array.hpp b/neosensor/libraries/ArduinoJson/src/ArduinoJson/Polyfills/static_array.hpp
deleted file mode 100644
index b2ebbf6b..00000000
--- a/neosensor/libraries/ArduinoJson/src/ArduinoJson/Polyfills/static_array.hpp
+++ /dev/null
@@ -1,30 +0,0 @@
-// ArduinoJson - https://arduinojson.org
-// Copyright © 2014-2022, Benoit BLANCHON
-// MIT License
-
-#pragma once
-
-#include <ArduinoJson/Configuration.hpp>
-
-#if ARDUINOJSON_ENABLE_PROGMEM
-
-#  include <ArduinoJson/Polyfills/pgmspace_generic.hpp>
-
-#  ifndef ARDUINOJSON_DEFINE_PROGMEM_ARRAY
-#    define ARDUINOJSON_DEFINE_PROGMEM_ARRAY(type, name, value) \
-      static type const name[] PROGMEM = value;
-#  endif
-
-#  define ARDUINOJSON_DEFINE_STATIC_ARRAY ARDUINOJSON_DEFINE_PROGMEM_ARRAY
-
-#  define ARDUINOJSON_READ_STATIC_ARRAY(type, name, index) \
-    pgm_read<type>(name + index)
-
-#else  // i.e. ARDUINOJSON_ENABLE_PROGMEM == 0
-
-#  define ARDUINOJSON_DEFINE_STATIC_ARRAY(type, name, value) \
-    static type const name[] = value;
-
-#  define ARDUINOJSON_READ_STATIC_ARRAY(type, name, index) name[index]
-
-#endif
diff --git a/neosensor/libraries/ArduinoJson/src/ArduinoJson/Polyfills/type_traits.hpp b/neosensor/libraries/ArduinoJson/src/ArduinoJson/Polyfills/type_traits.hpp
index f7afcb78..8b61e7c9 100644
--- a/neosensor/libraries/ArduinoJson/src/ArduinoJson/Polyfills/type_traits.hpp
+++ b/neosensor/libraries/ArduinoJson/src/ArduinoJson/Polyfills/type_traits.hpp
@@ -1,5 +1,5 @@
 // ArduinoJson - https://arduinojson.org
-// Copyright © 2014-2022, Benoit BLANCHON
+// Copyright © 2014-2023, Benoit BLANCHON
 // MIT License
 
 #pragma once
diff --git a/neosensor/libraries/ArduinoJson/src/ArduinoJson/Polyfills/type_traits/conditional.hpp b/neosensor/libraries/ArduinoJson/src/ArduinoJson/Polyfills/type_traits/conditional.hpp
index 0f2dc580..c481bcb8 100644
--- a/neosensor/libraries/ArduinoJson/src/ArduinoJson/Polyfills/type_traits/conditional.hpp
+++ b/neosensor/libraries/ArduinoJson/src/ArduinoJson/Polyfills/type_traits/conditional.hpp
@@ -1,12 +1,12 @@
 // ArduinoJson - https://arduinojson.org
-// Copyright © 2014-2022, Benoit BLANCHON
+// Copyright © 2014-2023, Benoit BLANCHON
 // MIT License
 
 #pragma once
 
 #include <ArduinoJson/Namespace.hpp>
 
-namespace ARDUINOJSON_NAMESPACE {
+ARDUINOJSON_BEGIN_PRIVATE_NAMESPACE
 
 template <bool Condition, class TrueType, class FalseType>
 struct conditional {
@@ -17,4 +17,5 @@ template <class TrueType, class FalseType>
 struct conditional<false, TrueType, FalseType> {
   typedef FalseType type;
 };
-}  // namespace ARDUINOJSON_NAMESPACE
+
+ARDUINOJSON_END_PRIVATE_NAMESPACE
diff --git a/neosensor/libraries/ArduinoJson/src/ArduinoJson/Polyfills/type_traits/declval.hpp b/neosensor/libraries/ArduinoJson/src/ArduinoJson/Polyfills/type_traits/declval.hpp
index 4dd5ed92..41f63e60 100644
--- a/neosensor/libraries/ArduinoJson/src/ArduinoJson/Polyfills/type_traits/declval.hpp
+++ b/neosensor/libraries/ArduinoJson/src/ArduinoJson/Polyfills/type_traits/declval.hpp
@@ -1,14 +1,14 @@
 // ArduinoJson - https://arduinojson.org
-// Copyright © 2014-2022, Benoit BLANCHON
+// Copyright © 2014-2023, Benoit BLANCHON
 // MIT License
 
 #pragma once
 
 #include <ArduinoJson/Namespace.hpp>
 
-namespace ARDUINOJSON_NAMESPACE {
+ARDUINOJSON_BEGIN_PRIVATE_NAMESPACE
 
 template <typename T>
-T declval();
+T&& declval();
 
-}  // namespace ARDUINOJSON_NAMESPACE
+ARDUINOJSON_END_PRIVATE_NAMESPACE
diff --git a/neosensor/libraries/ArduinoJson/src/ArduinoJson/Polyfills/type_traits/enable_if.hpp b/neosensor/libraries/ArduinoJson/src/ArduinoJson/Polyfills/type_traits/enable_if.hpp
index 3eec34cf..9d0c530c 100644
--- a/neosensor/libraries/ArduinoJson/src/ArduinoJson/Polyfills/type_traits/enable_if.hpp
+++ b/neosensor/libraries/ArduinoJson/src/ArduinoJson/Polyfills/type_traits/enable_if.hpp
@@ -1,12 +1,12 @@
 // ArduinoJson - https://arduinojson.org
-// Copyright © 2014-2022, Benoit BLANCHON
+// Copyright © 2014-2023, Benoit BLANCHON
 // MIT License
 
 #pragma once
 
 #include <ArduinoJson/Namespace.hpp>
 
-namespace ARDUINOJSON_NAMESPACE {
+ARDUINOJSON_BEGIN_PRIVATE_NAMESPACE
 
 // A meta-function that return the type T if Condition is true.
 template <bool Condition, typename T = void>
@@ -16,4 +16,5 @@ template <typename T>
 struct enable_if<true, T> {
   typedef T type;
 };
-}  // namespace ARDUINOJSON_NAMESPACE
+
+ARDUINOJSON_END_PRIVATE_NAMESPACE
diff --git a/neosensor/libraries/ArduinoJson/src/ArduinoJson/Polyfills/type_traits/integral_constant.hpp b/neosensor/libraries/ArduinoJson/src/ArduinoJson/Polyfills/type_traits/integral_constant.hpp
index f2585392..e4e224ef 100644
--- a/neosensor/libraries/ArduinoJson/src/ArduinoJson/Polyfills/type_traits/integral_constant.hpp
+++ b/neosensor/libraries/ArduinoJson/src/ArduinoJson/Polyfills/type_traits/integral_constant.hpp
@@ -1,12 +1,12 @@
 // ArduinoJson - https://arduinojson.org
-// Copyright © 2014-2022, Benoit BLANCHON
+// Copyright © 2014-2023, Benoit BLANCHON
 // MIT License
 
 #pragma once
 
 #include <ArduinoJson/Namespace.hpp>
 
-namespace ARDUINOJSON_NAMESPACE {
+ARDUINOJSON_BEGIN_PRIVATE_NAMESPACE
 
 template <typename T, T v>
 struct integral_constant {
@@ -16,4 +16,4 @@ struct integral_constant {
 typedef integral_constant<bool, true> true_type;
 typedef integral_constant<bool, false> false_type;
 
-}  // namespace ARDUINOJSON_NAMESPACE
+ARDUINOJSON_END_PRIVATE_NAMESPACE
diff --git a/neosensor/libraries/ArduinoJson/src/ArduinoJson/Polyfills/type_traits/is_array.hpp b/neosensor/libraries/ArduinoJson/src/ArduinoJson/Polyfills/type_traits/is_array.hpp
index 935671d8..470031be 100644
--- a/neosensor/libraries/ArduinoJson/src/ArduinoJson/Polyfills/type_traits/is_array.hpp
+++ b/neosensor/libraries/ArduinoJson/src/ArduinoJson/Polyfills/type_traits/is_array.hpp
@@ -1,5 +1,5 @@
 // ArduinoJson - https://arduinojson.org
-// Copyright © 2014-2022, Benoit BLANCHON
+// Copyright © 2014-2023, Benoit BLANCHON
 // MIT License
 
 #pragma once
@@ -8,7 +8,7 @@
 
 #include <stddef.h>  // size_t
 
-namespace ARDUINOJSON_NAMESPACE {
+ARDUINOJSON_BEGIN_PRIVATE_NAMESPACE
 
 template <typename T>
 struct is_array : false_type {};
@@ -18,4 +18,5 @@ struct is_array<T[]> : true_type {};
 
 template <typename T, size_t N>
 struct is_array<T[N]> : true_type {};
-}  // namespace ARDUINOJSON_NAMESPACE
+
+ARDUINOJSON_END_PRIVATE_NAMESPACE
diff --git a/neosensor/libraries/ArduinoJson/src/ArduinoJson/Polyfills/type_traits/is_base_of.hpp b/neosensor/libraries/ArduinoJson/src/ArduinoJson/Polyfills/type_traits/is_base_of.hpp
index 8936bd9b..61820a00 100644
--- a/neosensor/libraries/ArduinoJson/src/ArduinoJson/Polyfills/type_traits/is_base_of.hpp
+++ b/neosensor/libraries/ArduinoJson/src/ArduinoJson/Polyfills/type_traits/is_base_of.hpp
@@ -1,12 +1,14 @@
 // ArduinoJson - https://arduinojson.org
-// Copyright © 2014-2022, Benoit BLANCHON
+// Copyright © 2014-2023, Benoit BLANCHON
 // MIT License
 
 #pragma once
 
 #include <ArduinoJson/Namespace.hpp>
 
-namespace ARDUINOJSON_NAMESPACE {
+#include "remove_reference.hpp"
+
+ARDUINOJSON_BEGIN_PRIVATE_NAMESPACE
 
 // A meta-function that returns true if Derived inherits from TBase is an
 // integral type.
@@ -18,6 +20,8 @@ class is_base_of {
 
  public:
   static const bool value =
-      sizeof(probe(reinterpret_cast<TDerived*>(0))) == sizeof(int);
+      sizeof(probe(reinterpret_cast<typename remove_reference<TDerived>::type*>(
+          0))) == sizeof(int);
 };
-}  // namespace ARDUINOJSON_NAMESPACE
+
+ARDUINOJSON_END_PRIVATE_NAMESPACE
diff --git a/neosensor/libraries/ArduinoJson/src/ArduinoJson/Polyfills/type_traits/is_class.hpp b/neosensor/libraries/ArduinoJson/src/ArduinoJson/Polyfills/type_traits/is_class.hpp
index a586c3c5..f2b3e27a 100644
--- a/neosensor/libraries/ArduinoJson/src/ArduinoJson/Polyfills/type_traits/is_class.hpp
+++ b/neosensor/libraries/ArduinoJson/src/ArduinoJson/Polyfills/type_traits/is_class.hpp
@@ -1,12 +1,12 @@
 // ArduinoJson - https://arduinojson.org
-// Copyright © 2014-2022, Benoit BLANCHON
+// Copyright © 2014-2023, Benoit BLANCHON
 // MIT License
 
 #pragma once
 
 #include "declval.hpp"
 
-namespace ARDUINOJSON_NAMESPACE {
+ARDUINOJSON_BEGIN_PRIVATE_NAMESPACE
 
 template <typename T>
 struct is_class {
@@ -20,4 +20,4 @@ struct is_class {
   static const bool value = sizeof(probe<T>(0)) == sizeof(int);
 };
 
-}  // namespace ARDUINOJSON_NAMESPACE
+ARDUINOJSON_END_PRIVATE_NAMESPACE
diff --git a/neosensor/libraries/ArduinoJson/src/ArduinoJson/Polyfills/type_traits/is_const.hpp b/neosensor/libraries/ArduinoJson/src/ArduinoJson/Polyfills/type_traits/is_const.hpp
index f66d1420..330134fd 100644
--- a/neosensor/libraries/ArduinoJson/src/ArduinoJson/Polyfills/type_traits/is_const.hpp
+++ b/neosensor/libraries/ArduinoJson/src/ArduinoJson/Polyfills/type_traits/is_const.hpp
@@ -1,12 +1,12 @@
 // ArduinoJson - https://arduinojson.org
-// Copyright © 2014-2022, Benoit BLANCHON
+// Copyright © 2014-2023, Benoit BLANCHON
 // MIT License
 
 #pragma once
 
 #include "integral_constant.hpp"
 
-namespace ARDUINOJSON_NAMESPACE {
+ARDUINOJSON_BEGIN_PRIVATE_NAMESPACE
 
 // A meta-function that return the type T without the const modifier
 template <typename T>
@@ -14,4 +14,5 @@ struct is_const : false_type {};
 
 template <typename T>
 struct is_const<const T> : true_type {};
-}  // namespace ARDUINOJSON_NAMESPACE
+
+ARDUINOJSON_END_PRIVATE_NAMESPACE
diff --git a/neosensor/libraries/ArduinoJson/src/ArduinoJson/Polyfills/type_traits/is_convertible.hpp b/neosensor/libraries/ArduinoJson/src/ArduinoJson/Polyfills/type_traits/is_convertible.hpp
index ef18cf24..7c4810f3 100644
--- a/neosensor/libraries/ArduinoJson/src/ArduinoJson/Polyfills/type_traits/is_convertible.hpp
+++ b/neosensor/libraries/ArduinoJson/src/ArduinoJson/Polyfills/type_traits/is_convertible.hpp
@@ -1,5 +1,5 @@
 // ArduinoJson - https://arduinojson.org
-// Copyright © 2014-2022, Benoit BLANCHON
+// Copyright © 2014-2023, Benoit BLANCHON
 // MIT License
 
 #pragma once
@@ -19,7 +19,7 @@
 #endif
 // clang-format on
 
-namespace ARDUINOJSON_NAMESPACE {
+ARDUINOJSON_BEGIN_PRIVATE_NAMESPACE
 
 template <typename From, typename To>
 struct is_convertible {
@@ -27,13 +27,13 @@ struct is_convertible {
   static int probe(To);
   static char probe(...);
 
-  static From& _from;
+  static From& from_;
 
  public:
-  static const bool value = sizeof(probe(_from)) == sizeof(int);
+  static const bool value = sizeof(probe(from_)) == sizeof(int);
 };
 
-}  // namespace ARDUINOJSON_NAMESPACE
+ARDUINOJSON_END_PRIVATE_NAMESPACE
 
 #ifdef _MSC_VER
 #  pragma warning(pop)
diff --git a/neosensor/libraries/ArduinoJson/src/ArduinoJson/Polyfills/type_traits/is_enum.hpp b/neosensor/libraries/ArduinoJson/src/ArduinoJson/Polyfills/type_traits/is_enum.hpp
index de6663a9..a6668796 100644
--- a/neosensor/libraries/ArduinoJson/src/ArduinoJson/Polyfills/type_traits/is_enum.hpp
+++ b/neosensor/libraries/ArduinoJson/src/ArduinoJson/Polyfills/type_traits/is_enum.hpp
@@ -1,5 +1,5 @@
 // ArduinoJson - https://arduinojson.org
-// Copyright © 2014-2022, Benoit BLANCHON
+// Copyright © 2014-2023, Benoit BLANCHON
 // MIT License
 
 #pragma once
@@ -10,7 +10,7 @@
 #include "is_integral.hpp"
 #include "is_same.hpp"
 
-namespace ARDUINOJSON_NAMESPACE {
+ARDUINOJSON_BEGIN_PRIVATE_NAMESPACE
 
 template <typename T>
 struct is_enum {
@@ -19,4 +19,4 @@ struct is_enum {
                             !is_floating_point<T>::value;
 };
 
-}  // namespace ARDUINOJSON_NAMESPACE
+ARDUINOJSON_END_PRIVATE_NAMESPACE
diff --git a/neosensor/libraries/ArduinoJson/src/ArduinoJson/Polyfills/type_traits/is_floating_point.hpp b/neosensor/libraries/ArduinoJson/src/ArduinoJson/Polyfills/type_traits/is_floating_point.hpp
index c725eb84..4a58dfa0 100644
--- a/neosensor/libraries/ArduinoJson/src/ArduinoJson/Polyfills/type_traits/is_floating_point.hpp
+++ b/neosensor/libraries/ArduinoJson/src/ArduinoJson/Polyfills/type_traits/is_floating_point.hpp
@@ -1,5 +1,5 @@
 // ArduinoJson - https://arduinojson.org
-// Copyright © 2014-2022, Benoit BLANCHON
+// Copyright © 2014-2023, Benoit BLANCHON
 // MIT License
 
 #pragma once
@@ -8,7 +8,7 @@
 #include "is_same.hpp"
 #include "remove_cv.hpp"
 
-namespace ARDUINOJSON_NAMESPACE {
+ARDUINOJSON_BEGIN_PRIVATE_NAMESPACE
 
 template <class T>
 struct is_floating_point
@@ -17,4 +17,4 @@ struct is_floating_point
           is_same<float, typename remove_cv<T>::type>::value ||
               is_same<double, typename remove_cv<T>::type>::value> {};
 
-}  // namespace ARDUINOJSON_NAMESPACE
+ARDUINOJSON_END_PRIVATE_NAMESPACE
diff --git a/neosensor/libraries/ArduinoJson/src/ArduinoJson/Polyfills/type_traits/is_integral.hpp b/neosensor/libraries/ArduinoJson/src/ArduinoJson/Polyfills/type_traits/is_integral.hpp
index fc0da04a..85d18326 100644
--- a/neosensor/libraries/ArduinoJson/src/ArduinoJson/Polyfills/type_traits/is_integral.hpp
+++ b/neosensor/libraries/ArduinoJson/src/ArduinoJson/Polyfills/type_traits/is_integral.hpp
@@ -1,5 +1,5 @@
 // ArduinoJson - https://arduinojson.org
-// Copyright © 2014-2022, Benoit BLANCHON
+// Copyright © 2014-2023, Benoit BLANCHON
 // MIT License
 
 #pragma once
@@ -10,7 +10,7 @@
 #include "is_same.hpp"
 #include "remove_cv.hpp"
 
-namespace ARDUINOJSON_NAMESPACE {
+ARDUINOJSON_BEGIN_PRIVATE_NAMESPACE
 
 // clang-format off
 template <typename T>
@@ -23,15 +23,10 @@ struct is_integral : integral_constant<bool,
     is_same<typename remove_cv<T>::type, unsigned int>::value ||
     is_same<typename remove_cv<T>::type, signed long>::value ||
     is_same<typename remove_cv<T>::type, unsigned long>::value ||
-#if ARDUINOJSON_HAS_LONG_LONG
     is_same<typename remove_cv<T>::type, signed long long>::value ||
     is_same<typename remove_cv<T>::type, unsigned long long>::value ||
-#endif
-#if ARDUINOJSON_HAS_INT64
-    is_same<typename remove_cv<T>::type, signed __int64>::value ||
-    is_same<typename remove_cv<T>::type, unsigned __int64>::value ||
-#endif
     is_same<typename remove_cv<T>::type, char>::value ||
     is_same<typename remove_cv<T>::type, bool>::value> {};
 // clang-format on
-}  // namespace ARDUINOJSON_NAMESPACE
+
+ARDUINOJSON_END_PRIVATE_NAMESPACE
diff --git a/neosensor/libraries/ArduinoJson/src/ArduinoJson/Polyfills/type_traits/is_pointer.hpp b/neosensor/libraries/ArduinoJson/src/ArduinoJson/Polyfills/type_traits/is_pointer.hpp
index 8a99cdb6..2c200da2 100644
--- a/neosensor/libraries/ArduinoJson/src/ArduinoJson/Polyfills/type_traits/is_pointer.hpp
+++ b/neosensor/libraries/ArduinoJson/src/ArduinoJson/Polyfills/type_traits/is_pointer.hpp
@@ -1,16 +1,17 @@
 // ArduinoJson - https://arduinojson.org
-// Copyright © 2014-2022, Benoit BLANCHON
+// Copyright © 2014-2023, Benoit BLANCHON
 // MIT License
 
 #pragma once
 
 #include "integral_constant.hpp"
 
-namespace ARDUINOJSON_NAMESPACE {
+ARDUINOJSON_BEGIN_PRIVATE_NAMESPACE
 
 template <typename T>
 struct is_pointer : false_type {};
 
 template <typename T>
 struct is_pointer<T*> : true_type {};
-}  // namespace ARDUINOJSON_NAMESPACE
+
+ARDUINOJSON_END_PRIVATE_NAMESPACE
diff --git a/neosensor/libraries/ArduinoJson/src/ArduinoJson/Polyfills/type_traits/is_same.hpp b/neosensor/libraries/ArduinoJson/src/ArduinoJson/Polyfills/type_traits/is_same.hpp
index 956db6b4..71e66892 100644
--- a/neosensor/libraries/ArduinoJson/src/ArduinoJson/Polyfills/type_traits/is_same.hpp
+++ b/neosensor/libraries/ArduinoJson/src/ArduinoJson/Polyfills/type_traits/is_same.hpp
@@ -1,12 +1,12 @@
 // ArduinoJson - https://arduinojson.org
-// Copyright © 2014-2022, Benoit BLANCHON
+// Copyright © 2014-2023, Benoit BLANCHON
 // MIT License
 
 #pragma once
 
 #include "integral_constant.hpp"
 
-namespace ARDUINOJSON_NAMESPACE {
+ARDUINOJSON_BEGIN_PRIVATE_NAMESPACE
 
 // A meta-function that returns true if types T and U are the same.
 template <typename T, typename U>
@@ -14,4 +14,5 @@ struct is_same : false_type {};
 
 template <typename T>
 struct is_same<T, T> : true_type {};
-}  // namespace ARDUINOJSON_NAMESPACE
+
+ARDUINOJSON_END_PRIVATE_NAMESPACE
diff --git a/neosensor/libraries/ArduinoJson/src/ArduinoJson/Polyfills/type_traits/is_signed.hpp b/neosensor/libraries/ArduinoJson/src/ArduinoJson/Polyfills/type_traits/is_signed.hpp
index d6c7016a..eb3cc624 100644
--- a/neosensor/libraries/ArduinoJson/src/ArduinoJson/Polyfills/type_traits/is_signed.hpp
+++ b/neosensor/libraries/ArduinoJson/src/ArduinoJson/Polyfills/type_traits/is_signed.hpp
@@ -1,5 +1,5 @@
 // ArduinoJson - https://arduinojson.org
-// Copyright © 2014-2022, Benoit BLANCHON
+// Copyright © 2014-2023, Benoit BLANCHON
 // MIT License
 
 #pragma once
@@ -8,23 +8,19 @@
 #include "is_same.hpp"
 #include "remove_cv.hpp"
 
-namespace ARDUINOJSON_NAMESPACE {
+ARDUINOJSON_BEGIN_PRIVATE_NAMESPACE
 
 // clang-format off
 template <typename T>
-struct is_signed : integral_constant<bool, 
+struct is_signed : integral_constant<bool,
     is_same<typename remove_cv<T>::type, char>::value ||
     is_same<typename remove_cv<T>::type, signed char>::value ||
     is_same<typename remove_cv<T>::type, signed short>::value ||
     is_same<typename remove_cv<T>::type, signed int>::value ||
     is_same<typename remove_cv<T>::type, signed long>::value ||
-#if ARDUINOJSON_HAS_LONG_LONG
     is_same<typename remove_cv<T>::type, signed long long>::value ||
-#endif
-#if ARDUINOJSON_HAS_INT64
-    is_same<typename remove_cv<T>::type, signed __int64>::value ||
-#endif
     is_same<typename remove_cv<T>::type, float>::value ||
     is_same<typename remove_cv<T>::type, double>::value> {};
 // clang-format on
-}  // namespace ARDUINOJSON_NAMESPACE
+
+ARDUINOJSON_END_PRIVATE_NAMESPACE
diff --git a/neosensor/libraries/ArduinoJson/src/ArduinoJson/Polyfills/type_traits/is_unsigned.hpp b/neosensor/libraries/ArduinoJson/src/ArduinoJson/Polyfills/type_traits/is_unsigned.hpp
index 84b7d051..803eaea0 100644
--- a/neosensor/libraries/ArduinoJson/src/ArduinoJson/Polyfills/type_traits/is_unsigned.hpp
+++ b/neosensor/libraries/ArduinoJson/src/ArduinoJson/Polyfills/type_traits/is_unsigned.hpp
@@ -1,5 +1,5 @@
 // ArduinoJson - https://arduinojson.org
-// Copyright © 2014-2022, Benoit BLANCHON
+// Copyright © 2014-2023, Benoit BLANCHON
 // MIT License
 
 #pragma once
@@ -8,7 +8,7 @@
 #include "is_same.hpp"
 #include "remove_cv.hpp"
 
-namespace ARDUINOJSON_NAMESPACE {
+ARDUINOJSON_BEGIN_PRIVATE_NAMESPACE
 
 // clang-format off
 template <typename T>
@@ -17,12 +17,8 @@ struct is_unsigned : integral_constant<bool,
     is_same<typename remove_cv<T>::type, unsigned short>::value ||
     is_same<typename remove_cv<T>::type, unsigned int>::value ||
     is_same<typename remove_cv<T>::type, unsigned long>::value ||
-#if ARDUINOJSON_HAS_INT64
-    is_same<typename remove_cv<T>::type, unsigned __int64>::value ||
-#endif
-#if ARDUINOJSON_HAS_LONG_LONG
     is_same<typename remove_cv<T>::type, unsigned long long>::value ||
-#endif
     is_same<typename remove_cv<T>::type, bool>::value> {};
 // clang-format on
-}  // namespace ARDUINOJSON_NAMESPACE
+
+ARDUINOJSON_END_PRIVATE_NAMESPACE
diff --git a/neosensor/libraries/ArduinoJson/src/ArduinoJson/Polyfills/type_traits/make_unsigned.hpp b/neosensor/libraries/ArduinoJson/src/ArduinoJson/Polyfills/type_traits/make_unsigned.hpp
index f7fc3712..84798487 100644
--- a/neosensor/libraries/ArduinoJson/src/ArduinoJson/Polyfills/type_traits/make_unsigned.hpp
+++ b/neosensor/libraries/ArduinoJson/src/ArduinoJson/Polyfills/type_traits/make_unsigned.hpp
@@ -1,11 +1,11 @@
 // ArduinoJson - https://arduinojson.org
-// Copyright © 2014-2022, Benoit BLANCHON
+// Copyright © 2014-2023, Benoit BLANCHON
 // MIT License
 
 #pragma once
 
 #include "type_identity.hpp"
-namespace ARDUINOJSON_NAMESPACE {
+ARDUINOJSON_BEGIN_PRIVATE_NAMESPACE
 
 template <typename T>
 struct make_unsigned;
@@ -33,17 +33,9 @@ struct make_unsigned<signed long> : type_identity<unsigned long> {};
 template <>
 struct make_unsigned<unsigned long> : type_identity<unsigned long> {};
 
-#if ARDUINOJSON_HAS_LONG_LONG
 template <>
 struct make_unsigned<signed long long> : type_identity<unsigned long long> {};
 template <>
 struct make_unsigned<unsigned long long> : type_identity<unsigned long long> {};
-#endif
 
-#if ARDUINOJSON_HAS_INT64
-template <>
-struct make_unsigned<signed __int64> : type_identity<unsigned __int64> {};
-template <>
-struct make_unsigned<unsigned __int64> : type_identity<unsigned __int64> {};
-#endif
-}  // namespace ARDUINOJSON_NAMESPACE
+ARDUINOJSON_END_PRIVATE_NAMESPACE
diff --git a/neosensor/libraries/ArduinoJson/src/ArduinoJson/Polyfills/type_traits/make_void.hpp b/neosensor/libraries/ArduinoJson/src/ArduinoJson/Polyfills/type_traits/make_void.hpp
index bd2d7448..b20d424d 100644
--- a/neosensor/libraries/ArduinoJson/src/ArduinoJson/Polyfills/type_traits/make_void.hpp
+++ b/neosensor/libraries/ArduinoJson/src/ArduinoJson/Polyfills/type_traits/make_void.hpp
@@ -1,14 +1,14 @@
 // ArduinoJson - https://arduinojson.org
-// Copyright © 2014-2022, Benoit BLANCHON
+// Copyright © 2014-2023, Benoit BLANCHON
 // MIT License
 
 #pragma once
 
-namespace ARDUINOJSON_NAMESPACE {
+ARDUINOJSON_BEGIN_PRIVATE_NAMESPACE
 
 template <class = void>
 struct make_void {
   typedef void type;
 };
 
-}  // namespace ARDUINOJSON_NAMESPACE
+ARDUINOJSON_END_PRIVATE_NAMESPACE
diff --git a/neosensor/libraries/ArduinoJson/src/ArduinoJson/Polyfills/type_traits/remove_const.hpp b/neosensor/libraries/ArduinoJson/src/ArduinoJson/Polyfills/type_traits/remove_const.hpp
index 77fb438d..2592566c 100644
--- a/neosensor/libraries/ArduinoJson/src/ArduinoJson/Polyfills/type_traits/remove_const.hpp
+++ b/neosensor/libraries/ArduinoJson/src/ArduinoJson/Polyfills/type_traits/remove_const.hpp
@@ -1,12 +1,12 @@
 // ArduinoJson - https://arduinojson.org
-// Copyright © 2014-2022, Benoit BLANCHON
+// Copyright © 2014-2023, Benoit BLANCHON
 // MIT License
 
 #pragma once
 
 #include <ArduinoJson/Namespace.hpp>
 
-namespace ARDUINOJSON_NAMESPACE {
+ARDUINOJSON_BEGIN_PRIVATE_NAMESPACE
 
 // A meta-function that return the type T without the const modifier
 template <typename T>
@@ -17,4 +17,5 @@ template <typename T>
 struct remove_const<const T> {
   typedef T type;
 };
-}  // namespace ARDUINOJSON_NAMESPACE
+
+ARDUINOJSON_END_PRIVATE_NAMESPACE
diff --git a/neosensor/libraries/ArduinoJson/src/ArduinoJson/Polyfills/type_traits/remove_cv.hpp b/neosensor/libraries/ArduinoJson/src/ArduinoJson/Polyfills/type_traits/remove_cv.hpp
index 64f6e109..70ceed34 100644
--- a/neosensor/libraries/ArduinoJson/src/ArduinoJson/Polyfills/type_traits/remove_cv.hpp
+++ b/neosensor/libraries/ArduinoJson/src/ArduinoJson/Polyfills/type_traits/remove_cv.hpp
@@ -1,12 +1,12 @@
 // ArduinoJson - https://arduinojson.org
-// Copyright © 2014-2022, Benoit BLANCHON
+// Copyright © 2014-2023, Benoit BLANCHON
 // MIT License
 
 #pragma once
 
 #include <ArduinoJson/Namespace.hpp>
 
-namespace ARDUINOJSON_NAMESPACE {
+ARDUINOJSON_BEGIN_PRIVATE_NAMESPACE
 
 template <typename T>
 struct remove_cv {
@@ -24,4 +24,5 @@ template <typename T>
 struct remove_cv<const volatile T> {
   typedef T type;
 };
-}  // namespace ARDUINOJSON_NAMESPACE
+
+ARDUINOJSON_END_PRIVATE_NAMESPACE
diff --git a/neosensor/libraries/ArduinoJson/src/ArduinoJson/Polyfills/type_traits/remove_reference.hpp b/neosensor/libraries/ArduinoJson/src/ArduinoJson/Polyfills/type_traits/remove_reference.hpp
index b63d1ae9..263c7300 100644
--- a/neosensor/libraries/ArduinoJson/src/ArduinoJson/Polyfills/type_traits/remove_reference.hpp
+++ b/neosensor/libraries/ArduinoJson/src/ArduinoJson/Polyfills/type_traits/remove_reference.hpp
@@ -1,12 +1,12 @@
 // ArduinoJson - https://arduinojson.org
-// Copyright © 2014-2022, Benoit BLANCHON
+// Copyright © 2014-2023, Benoit BLANCHON
 // MIT License
 
 #pragma once
 
 #include <ArduinoJson/Namespace.hpp>
 
-namespace ARDUINOJSON_NAMESPACE {
+ARDUINOJSON_BEGIN_PRIVATE_NAMESPACE
 
 // A meta-function that return the type T without the reference modifier.
 template <typename T>
@@ -17,4 +17,5 @@ template <typename T>
 struct remove_reference<T&> {
   typedef T type;
 };
-}  // namespace ARDUINOJSON_NAMESPACE
+
+ARDUINOJSON_END_PRIVATE_NAMESPACE
diff --git a/neosensor/libraries/ArduinoJson/src/ArduinoJson/Polyfills/type_traits/type_identity.hpp b/neosensor/libraries/ArduinoJson/src/ArduinoJson/Polyfills/type_traits/type_identity.hpp
index b797b517..4481bc7e 100644
--- a/neosensor/libraries/ArduinoJson/src/ArduinoJson/Polyfills/type_traits/type_identity.hpp
+++ b/neosensor/libraries/ArduinoJson/src/ArduinoJson/Polyfills/type_traits/type_identity.hpp
@@ -1,15 +1,16 @@
 // ArduinoJson - https://arduinojson.org
-// Copyright © 2014-2022, Benoit BLANCHON
+// Copyright © 2014-2023, Benoit BLANCHON
 // MIT License
 
 #pragma once
 
 #include "integral_constant.hpp"
 
-namespace ARDUINOJSON_NAMESPACE {
+ARDUINOJSON_BEGIN_PRIVATE_NAMESPACE
 
 template <typename T>
 struct type_identity {
   typedef T type;
 };
-}  // namespace ARDUINOJSON_NAMESPACE
+
+ARDUINOJSON_END_PRIVATE_NAMESPACE
diff --git a/neosensor/libraries/ArduinoJson/src/ArduinoJson/Polyfills/utility.hpp b/neosensor/libraries/ArduinoJson/src/ArduinoJson/Polyfills/utility.hpp
new file mode 100644
index 00000000..13fb3e70
--- /dev/null
+++ b/neosensor/libraries/ArduinoJson/src/ArduinoJson/Polyfills/utility.hpp
@@ -0,0 +1,16 @@
+// ArduinoJson - https://arduinojson.org
+// Copyright © 2014-2023, Benoit BLANCHON
+// MIT License
+
+#pragma once
+
+#include "type_traits.hpp"
+
+ARDUINOJSON_BEGIN_PRIVATE_NAMESPACE
+
+template <class T>
+T&& forward(typename remove_reference<T>::type& t) noexcept {
+  return static_cast<T&&>(t);
+}
+
+ARDUINOJSON_END_PRIVATE_NAMESPACE
diff --git a/neosensor/libraries/ArduinoJson/src/ArduinoJson/Serialization/CountingDecorator.hpp b/neosensor/libraries/ArduinoJson/src/ArduinoJson/Serialization/CountingDecorator.hpp
index 6fc4689d..b38a22df 100644
--- a/neosensor/libraries/ArduinoJson/src/ArduinoJson/Serialization/CountingDecorator.hpp
+++ b/neosensor/libraries/ArduinoJson/src/ArduinoJson/Serialization/CountingDecorator.hpp
@@ -1,33 +1,33 @@
 // ArduinoJson - https://arduinojson.org
-// Copyright © 2014-2022, Benoit BLANCHON
+// Copyright © 2014-2023, Benoit BLANCHON
 // MIT License
 
 #pragma once
 
 #include <ArduinoJson/Namespace.hpp>
 
-namespace ARDUINOJSON_NAMESPACE {
+ARDUINOJSON_BEGIN_PRIVATE_NAMESPACE
 
 template <typename TWriter>
 class CountingDecorator {
  public:
-  explicit CountingDecorator(TWriter& writer) : _writer(writer), _count(0) {}
+  explicit CountingDecorator(TWriter& writer) : writer_(writer), count_(0) {}
 
   void write(uint8_t c) {
-    _count += _writer.write(c);
+    count_ += writer_.write(c);
   }
 
   void write(const uint8_t* s, size_t n) {
-    _count += _writer.write(s, n);
+    count_ += writer_.write(s, n);
   }
 
   size_t count() const {
-    return _count;
+    return count_;
   }
 
  private:
-  TWriter _writer;
-  size_t _count;
+  TWriter writer_;
+  size_t count_;
 };
 
-}  // namespace ARDUINOJSON_NAMESPACE
+ARDUINOJSON_END_PRIVATE_NAMESPACE
diff --git a/neosensor/libraries/ArduinoJson/src/ArduinoJson/Serialization/Writer.hpp b/neosensor/libraries/ArduinoJson/src/ArduinoJson/Serialization/Writer.hpp
index e69247c7..f6ba225d 100644
--- a/neosensor/libraries/ArduinoJson/src/ArduinoJson/Serialization/Writer.hpp
+++ b/neosensor/libraries/ArduinoJson/src/ArduinoJson/Serialization/Writer.hpp
@@ -1,32 +1,32 @@
 // ArduinoJson - https://arduinojson.org
-// Copyright © 2014-2022, Benoit BLANCHON
+// Copyright © 2014-2023, Benoit BLANCHON
 // MIT License
 
 #pragma once
 
 #include <ArduinoJson/Namespace.hpp>
 
-namespace ARDUINOJSON_NAMESPACE {
+ARDUINOJSON_BEGIN_PRIVATE_NAMESPACE
 
 // The default writer is a simple wrapper for Writers that are not copiable
 template <typename TDestination, typename Enable = void>
 class Writer {
  public:
-  explicit Writer(TDestination& dest) : _dest(&dest) {}
+  explicit Writer(TDestination& dest) : dest_(&dest) {}
 
   size_t write(uint8_t c) {
-    return _dest->write(c);
+    return dest_->write(c);
   }
 
   size_t write(const uint8_t* s, size_t n) {
-    return _dest->write(s, n);
+    return dest_->write(s, n);
   }
 
  private:
-  TDestination* _dest;
+  TDestination* dest_;
 };
 
-}  // namespace ARDUINOJSON_NAMESPACE
+ARDUINOJSON_END_PRIVATE_NAMESPACE
 
 #include <ArduinoJson/Serialization/Writers/StaticStringWriter.hpp>
 
diff --git a/neosensor/libraries/ArduinoJson/src/ArduinoJson/Serialization/Writers/ArduinoStringWriter.hpp b/neosensor/libraries/ArduinoJson/src/ArduinoJson/Serialization/Writers/ArduinoStringWriter.hpp
index 3ab1be3d..c6432487 100644
--- a/neosensor/libraries/ArduinoJson/src/ArduinoJson/Serialization/Writers/ArduinoStringWriter.hpp
+++ b/neosensor/libraries/ArduinoJson/src/ArduinoJson/Serialization/Writers/ArduinoStringWriter.hpp
@@ -1,20 +1,20 @@
 // ArduinoJson - https://arduinojson.org
-// Copyright © 2014-2022, Benoit BLANCHON
+// Copyright © 2014-2023, Benoit BLANCHON
 // MIT License
 
 #pragma once
 
 #include <Arduino.h>
 
-namespace ARDUINOJSON_NAMESPACE {
+ARDUINOJSON_BEGIN_PRIVATE_NAMESPACE
 
 template <>
-class Writer< ::String, void> {
+class Writer<::String, void> {
   static const size_t bufferCapacity = ARDUINOJSON_STRING_BUFFER_SIZE;
 
  public:
-  explicit Writer(::String& str) : _destination(&str) {
-    _size = 0;
+  explicit Writer(::String& str) : destination_(&str) {
+    size_ = 0;
   }
 
   ~Writer() {
@@ -22,10 +22,10 @@ class Writer< ::String, void> {
   }
 
   size_t write(uint8_t c) {
-    if (_size + 1 >= bufferCapacity)
+    if (size_ + 1 >= bufferCapacity)
       if (flush() != 0)
         return 0;
-    _buffer[_size++] = static_cast<char>(c);
+    buffer_[size_++] = static_cast<char>(c);
     return 1;
   }
 
@@ -37,17 +37,17 @@ class Writer< ::String, void> {
   }
 
   size_t flush() {
-    ARDUINOJSON_ASSERT(_size < bufferCapacity);
-    _buffer[_size] = 0;
-    if (_destination->concat(_buffer))
-      _size = 0;
-    return _size;
+    ARDUINOJSON_ASSERT(size_ < bufferCapacity);
+    buffer_[size_] = 0;
+    if (destination_->concat(buffer_))
+      size_ = 0;
+    return size_;
   }
 
  private:
-  ::String* _destination;
-  char _buffer[bufferCapacity];
-  size_t _size;
+  ::String* destination_;
+  char buffer_[bufferCapacity];
+  size_t size_;
 };
 
-}  // namespace ARDUINOJSON_NAMESPACE
+ARDUINOJSON_END_PRIVATE_NAMESPACE
diff --git a/neosensor/libraries/ArduinoJson/src/ArduinoJson/Serialization/Writers/DummyWriter.hpp b/neosensor/libraries/ArduinoJson/src/ArduinoJson/Serialization/Writers/DummyWriter.hpp
index 430865e9..ff35def7 100644
--- a/neosensor/libraries/ArduinoJson/src/ArduinoJson/Serialization/Writers/DummyWriter.hpp
+++ b/neosensor/libraries/ArduinoJson/src/ArduinoJson/Serialization/Writers/DummyWriter.hpp
@@ -1,12 +1,12 @@
 // ArduinoJson - https://arduinojson.org
-// Copyright © 2014-2022, Benoit BLANCHON
+// Copyright © 2014-2023, Benoit BLANCHON
 // MIT License
 
 #pragma once
 
 #include <ArduinoJson/Namespace.hpp>
 
-namespace ARDUINOJSON_NAMESPACE {
+ARDUINOJSON_BEGIN_PRIVATE_NAMESPACE
 
 class DummyWriter {
  public:
@@ -18,4 +18,5 @@ class DummyWriter {
     return n;
   }
 };
-}  // namespace ARDUINOJSON_NAMESPACE
+
+ARDUINOJSON_END_PRIVATE_NAMESPACE
diff --git a/neosensor/libraries/ArduinoJson/src/ArduinoJson/Serialization/Writers/PrintWriter.hpp b/neosensor/libraries/ArduinoJson/src/ArduinoJson/Serialization/Writers/PrintWriter.hpp
index cfac79fb..49f6a6e9 100644
--- a/neosensor/libraries/ArduinoJson/src/ArduinoJson/Serialization/Writers/PrintWriter.hpp
+++ b/neosensor/libraries/ArduinoJson/src/ArduinoJson/Serialization/Writers/PrintWriter.hpp
@@ -1,30 +1,30 @@
 // ArduinoJson - https://arduinojson.org
-// Copyright © 2014-2022, Benoit BLANCHON
+// Copyright © 2014-2023, Benoit BLANCHON
 // MIT License
 
 #pragma once
 
 #include <Arduino.h>
 
-namespace ARDUINOJSON_NAMESPACE {
+ARDUINOJSON_BEGIN_PRIVATE_NAMESPACE
 
 template <typename TDestination>
 class Writer<
     TDestination,
-    typename enable_if<is_base_of< ::Print, TDestination>::value>::type> {
+    typename enable_if<is_base_of<::Print, TDestination>::value>::type> {
  public:
-  explicit Writer(::Print& print) : _print(&print) {}
+  explicit Writer(::Print& print) : print_(&print) {}
 
   size_t write(uint8_t c) {
-    return _print->write(c);
+    return print_->write(c);
   }
 
   size_t write(const uint8_t* s, size_t n) {
-    return _print->write(s, n);
+    return print_->write(s, n);
   }
 
  private:
-  ::Print* _print;
+  ::Print* print_;
 };
 
-}  // namespace ARDUINOJSON_NAMESPACE
+ARDUINOJSON_END_PRIVATE_NAMESPACE
diff --git a/neosensor/libraries/ArduinoJson/src/ArduinoJson/Serialization/Writers/StaticStringWriter.hpp b/neosensor/libraries/ArduinoJson/src/ArduinoJson/Serialization/Writers/StaticStringWriter.hpp
index f55ddaff..169ebe19 100644
--- a/neosensor/libraries/ArduinoJson/src/ArduinoJson/Serialization/Writers/StaticStringWriter.hpp
+++ b/neosensor/libraries/ArduinoJson/src/ArduinoJson/Serialization/Writers/StaticStringWriter.hpp
@@ -1,12 +1,12 @@
 // ArduinoJson - https://arduinojson.org
-// Copyright © 2014-2022, Benoit BLANCHON
+// Copyright © 2014-2023, Benoit BLANCHON
 // MIT License
 
 #pragma once
 
 #include <ArduinoJson/Namespace.hpp>
 
-namespace ARDUINOJSON_NAMESPACE {
+ARDUINOJSON_BEGIN_PRIVATE_NAMESPACE
 
 class StaticStringWriter {
  public:
@@ -32,4 +32,5 @@ class StaticStringWriter {
   char* end;
   char* p;
 };
-}  // namespace ARDUINOJSON_NAMESPACE
+
+ARDUINOJSON_END_PRIVATE_NAMESPACE
diff --git a/neosensor/libraries/ArduinoJson/src/ArduinoJson/Serialization/Writers/StdStreamWriter.hpp b/neosensor/libraries/ArduinoJson/src/ArduinoJson/Serialization/Writers/StdStreamWriter.hpp
index e77b445b..fdfead73 100644
--- a/neosensor/libraries/ArduinoJson/src/ArduinoJson/Serialization/Writers/StdStreamWriter.hpp
+++ b/neosensor/libraries/ArduinoJson/src/ArduinoJson/Serialization/Writers/StdStreamWriter.hpp
@@ -1,32 +1,33 @@
 // ArduinoJson - https://arduinojson.org
-// Copyright © 2014-2022, Benoit BLANCHON
+// Copyright © 2014-2023, Benoit BLANCHON
 // MIT License
 
 #pragma once
 
 #include <ostream>
 
-namespace ARDUINOJSON_NAMESPACE {
+ARDUINOJSON_BEGIN_PRIVATE_NAMESPACE
 
 template <typename TDestination>
 class Writer<
     TDestination,
     typename enable_if<is_base_of<std::ostream, TDestination>::value>::type> {
  public:
-  explicit Writer(std::ostream& os) : _os(&os) {}
+  explicit Writer(std::ostream& os) : os_(&os) {}
 
   size_t write(uint8_t c) {
-    _os->put(static_cast<char>(c));
+    os_->put(static_cast<char>(c));
     return 1;
   }
 
   size_t write(const uint8_t* s, size_t n) {
-    _os->write(reinterpret_cast<const char*>(s),
+    os_->write(reinterpret_cast<const char*>(s),
                static_cast<std::streamsize>(n));
     return n;
   }
 
  private:
-  std::ostream* _os;
+  std::ostream* os_;
 };
-}  // namespace ARDUINOJSON_NAMESPACE
+
+ARDUINOJSON_END_PRIVATE_NAMESPACE
diff --git a/neosensor/libraries/ArduinoJson/src/ArduinoJson/Serialization/Writers/StdStringWriter.hpp b/neosensor/libraries/ArduinoJson/src/ArduinoJson/Serialization/Writers/StdStringWriter.hpp
index 2ca14505..1f8afaa8 100644
--- a/neosensor/libraries/ArduinoJson/src/ArduinoJson/Serialization/Writers/StdStringWriter.hpp
+++ b/neosensor/libraries/ArduinoJson/src/ArduinoJson/Serialization/Writers/StdStringWriter.hpp
@@ -1,5 +1,5 @@
 // ArduinoJson - https://arduinojson.org
-// Copyright © 2014-2022, Benoit BLANCHON
+// Copyright © 2014-2023, Benoit BLANCHON
 // MIT License
 
 #pragma once
@@ -7,34 +7,37 @@
 #include <ArduinoJson/Namespace.hpp>
 #include <ArduinoJson/Polyfills/type_traits.hpp>
 
-#include <string>
+ARDUINOJSON_BEGIN_PRIVATE_NAMESPACE
 
-namespace ARDUINOJSON_NAMESPACE {
+template <class...>
+using void_t = void;
 
-template <class T>
+template <class T, typename = void>
 struct is_std_string : false_type {};
 
-template <class TCharTraits, class TAllocator>
-struct is_std_string<std::basic_string<char, TCharTraits, TAllocator> >
+template <class T>
+struct is_std_string<
+    T, void_t<decltype(T().push_back('a')), decltype(T().append(""))>>
     : true_type {};
 
 template <typename TDestination>
 class Writer<TDestination,
              typename enable_if<is_std_string<TDestination>::value>::type> {
  public:
-  Writer(TDestination& str) : _str(&str) {}
+  Writer(TDestination& str) : str_(&str) {}
 
   size_t write(uint8_t c) {
-    _str->operator+=(static_cast<char>(c));
+    str_->push_back(static_cast<char>(c));
     return 1;
   }
 
   size_t write(const uint8_t* s, size_t n) {
-    _str->append(reinterpret_cast<const char*>(s), n);
+    str_->append(reinterpret_cast<const char*>(s), n);
     return n;
   }
 
  private:
-  TDestination* _str;
+  TDestination* str_;
 };
-}  // namespace ARDUINOJSON_NAMESPACE
+
+ARDUINOJSON_END_PRIVATE_NAMESPACE
diff --git a/neosensor/libraries/ArduinoJson/src/ArduinoJson/Serialization/measure.hpp b/neosensor/libraries/ArduinoJson/src/ArduinoJson/Serialization/measure.hpp
index b8172c57..5a71e104 100644
--- a/neosensor/libraries/ArduinoJson/src/ArduinoJson/Serialization/measure.hpp
+++ b/neosensor/libraries/ArduinoJson/src/ArduinoJson/Serialization/measure.hpp
@@ -1,5 +1,5 @@
 // ArduinoJson - https://arduinojson.org
-// Copyright © 2014-2022, Benoit BLANCHON
+// Copyright © 2014-2023, Benoit BLANCHON
 // MIT License
 
 #pragma once
@@ -7,13 +7,13 @@
 #include <ArduinoJson/Serialization/Writers/DummyWriter.hpp>
 #include <ArduinoJson/Variant/VariantFunctions.hpp>
 
-namespace ARDUINOJSON_NAMESPACE {
+ARDUINOJSON_BEGIN_PRIVATE_NAMESPACE
 
 template <template <typename> class TSerializer>
-size_t measure(JsonVariantConst source) {
+size_t measure(ArduinoJson::JsonVariantConst source) {
   DummyWriter dp;
   TSerializer<DummyWriter> serializer(dp);
   return variantAccept(VariantAttorney::getData(source), serializer);
 }
 
-}  // namespace ARDUINOJSON_NAMESPACE
+ARDUINOJSON_END_PRIVATE_NAMESPACE
diff --git a/neosensor/libraries/ArduinoJson/src/ArduinoJson/Serialization/serialize.hpp b/neosensor/libraries/ArduinoJson/src/ArduinoJson/Serialization/serialize.hpp
index cc57b4c6..1014a7d6 100644
--- a/neosensor/libraries/ArduinoJson/src/ArduinoJson/Serialization/serialize.hpp
+++ b/neosensor/libraries/ArduinoJson/src/ArduinoJson/Serialization/serialize.hpp
@@ -1,5 +1,5 @@
 // ArduinoJson - https://arduinojson.org
-// Copyright © 2014-2022, Benoit BLANCHON
+// Copyright © 2014-2023, Benoit BLANCHON
 // MIT License
 
 #pragma once
@@ -7,30 +7,33 @@
 #include <ArduinoJson/Serialization/Writer.hpp>
 #include <ArduinoJson/Variant/VariantFunctions.hpp>
 
-namespace ARDUINOJSON_NAMESPACE {
+ARDUINOJSON_BEGIN_PRIVATE_NAMESPACE
 
 template <template <typename> class TSerializer, typename TWriter>
-size_t doSerialize(JsonVariantConst source, TWriter writer) {
+size_t doSerialize(ArduinoJson::JsonVariantConst source, TWriter writer) {
   TSerializer<TWriter> serializer(writer);
   return variantAccept(VariantAttorney::getData(source), serializer);
 }
 
 template <template <typename> class TSerializer, typename TDestination>
-size_t serialize(JsonVariantConst source, TDestination& destination) {
+size_t serialize(ArduinoJson::JsonVariantConst source,
+                 TDestination& destination) {
   Writer<TDestination> writer(destination);
   return doSerialize<TSerializer>(source, writer);
 }
 
 template <template <typename> class TSerializer>
 typename enable_if<!TSerializer<StaticStringWriter>::producesText, size_t>::type
-serialize(JsonVariantConst source, void* buffer, size_t bufferSize) {
+serialize(ArduinoJson::JsonVariantConst source, void* buffer,
+          size_t bufferSize) {
   StaticStringWriter writer(reinterpret_cast<char*>(buffer), bufferSize);
   return doSerialize<TSerializer>(source, writer);
 }
 
 template <template <typename> class TSerializer>
 typename enable_if<TSerializer<StaticStringWriter>::producesText, size_t>::type
-serialize(JsonVariantConst source, void* buffer, size_t bufferSize) {
+serialize(ArduinoJson::JsonVariantConst source, void* buffer,
+          size_t bufferSize) {
   StaticStringWriter writer(reinterpret_cast<char*>(buffer), bufferSize);
   size_t n = doSerialize<TSerializer>(source, writer);
   // add null-terminator for text output (not counted in the size)
@@ -41,8 +44,8 @@ serialize(JsonVariantConst source, void* buffer, size_t bufferSize) {
 
 template <template <typename> class TSerializer, typename TChar, size_t N>
 typename enable_if<IsChar<TChar>::value, size_t>::type serialize(
-    JsonVariantConst source, TChar (&buffer)[N]) {
+    ArduinoJson::JsonVariantConst source, TChar (&buffer)[N]) {
   return serialize<TSerializer>(source, buffer, N);
 }
 
-}  // namespace ARDUINOJSON_NAMESPACE
+ARDUINOJSON_END_PRIVATE_NAMESPACE
diff --git a/neosensor/libraries/ArduinoJson/src/ArduinoJson/StringStorage/StringCopier.hpp b/neosensor/libraries/ArduinoJson/src/ArduinoJson/StringStorage/StringCopier.hpp
index e9624e59..bcd34898 100644
--- a/neosensor/libraries/ArduinoJson/src/ArduinoJson/StringStorage/StringCopier.hpp
+++ b/neosensor/libraries/ArduinoJson/src/ArduinoJson/StringStorage/StringCopier.hpp
@@ -1,28 +1,28 @@
 // ArduinoJson - https://arduinojson.org
-// Copyright © 2014-2022, Benoit BLANCHON
+// Copyright © 2014-2023, Benoit BLANCHON
 // MIT License
 
 #pragma once
 
 #include <ArduinoJson/Memory/MemoryPool.hpp>
 
-namespace ARDUINOJSON_NAMESPACE {
+ARDUINOJSON_BEGIN_PRIVATE_NAMESPACE
 
 class StringCopier {
  public:
-  StringCopier(MemoryPool* pool) : _pool(pool) {}
+  StringCopier(MemoryPool* pool) : pool_(pool) {}
 
   void startString() {
-    _pool->getFreeZone(&_ptr, &_capacity);
-    _size = 0;
-    if (_capacity == 0)
-      _pool->markAsOverflowed();
+    pool_->getFreeZone(&ptr_, &capacity_);
+    size_ = 0;
+    if (capacity_ == 0)
+      pool_->markAsOverflowed();
   }
 
   JsonString save() {
-    ARDUINOJSON_ASSERT(_ptr);
-    ARDUINOJSON_ASSERT(_size < _capacity);  // needs room for the terminator
-    return JsonString(_pool->saveStringFromFreeZone(_size), _size,
+    ARDUINOJSON_ASSERT(ptr_);
+    ARDUINOJSON_ASSERT(size_ < capacity_);  // needs room for the terminator
+    return JsonString(pool_->saveStringFromFreeZone(size_), size_,
                       JsonString::Copied);
   }
 
@@ -37,35 +37,36 @@ class StringCopier {
   }
 
   void append(char c) {
-    if (_size + 1 < _capacity)
-      _ptr[_size++] = c;
+    if (size_ + 1 < capacity_)
+      ptr_[size_++] = c;
     else
-      _pool->markAsOverflowed();
+      pool_->markAsOverflowed();
   }
 
   bool isValid() const {
-    return !_pool->overflowed();
+    return !pool_->overflowed();
   }
 
   size_t size() const {
-    return _size;
+    return size_;
   }
 
   JsonString str() const {
-    ARDUINOJSON_ASSERT(_ptr);
-    ARDUINOJSON_ASSERT(_size < _capacity);
-    _ptr[_size] = 0;
-    return JsonString(_ptr, _size, JsonString::Copied);
+    ARDUINOJSON_ASSERT(ptr_);
+    ARDUINOJSON_ASSERT(size_ < capacity_);
+    ptr_[size_] = 0;
+    return JsonString(ptr_, size_, JsonString::Copied);
   }
 
  private:
-  MemoryPool* _pool;
+  MemoryPool* pool_;
 
   // These fields aren't initialized by the constructor but startString()
   //
   // NOLINTNEXTLINE(clang-analyzer-optin.cplusplus.UninitializedObject)
-  char* _ptr;
+  char* ptr_;
   // NOLINTNEXTLINE(clang-analyzer-optin.cplusplus.UninitializedObject)
-  size_t _size, _capacity;
+  size_t size_, capacity_;
 };
-}  // namespace ARDUINOJSON_NAMESPACE
+
+ARDUINOJSON_END_PRIVATE_NAMESPACE
diff --git a/neosensor/libraries/ArduinoJson/src/ArduinoJson/StringStorage/StringMover.hpp b/neosensor/libraries/ArduinoJson/src/ArduinoJson/StringStorage/StringMover.hpp
index 688e9e27..584b2531 100644
--- a/neosensor/libraries/ArduinoJson/src/ArduinoJson/StringStorage/StringMover.hpp
+++ b/neosensor/libraries/ArduinoJson/src/ArduinoJson/StringStorage/StringMover.hpp
@@ -1,5 +1,5 @@
 // ArduinoJson - https://arduinojson.org
-// Copyright © 2014-2022, Benoit BLANCHON
+// Copyright © 2014-2023, Benoit BLANCHON
 // MIT License
 
 #pragma once
@@ -7,24 +7,24 @@
 #include <ArduinoJson/Namespace.hpp>
 #include <ArduinoJson/Strings/JsonString.hpp>
 
-namespace ARDUINOJSON_NAMESPACE {
+ARDUINOJSON_BEGIN_PRIVATE_NAMESPACE
 
 class StringMover {
  public:
-  StringMover(char* ptr) : _writePtr(ptr) {}
+  StringMover(char* ptr) : writePtr_(ptr) {}
 
   void startString() {
-    _startPtr = _writePtr;
+    startPtr_ = writePtr_;
   }
 
   FORCE_INLINE JsonString save() {
     JsonString s = str();
-    _writePtr++;
+    writePtr_++;
     return s;
   }
 
   void append(char c) {
-    *_writePtr++ = c;
+    *writePtr_++ = c;
   }
 
   bool isValid() const {
@@ -32,16 +32,17 @@ class StringMover {
   }
 
   JsonString str() const {
-    _writePtr[0] = 0;  // terminator
-    return JsonString(_startPtr, size(), JsonString::Linked);
+    writePtr_[0] = 0;  // terminator
+    return JsonString(startPtr_, size(), JsonString::Linked);
   }
 
   size_t size() const {
-    return size_t(_writePtr - _startPtr);
+    return size_t(writePtr_ - startPtr_);
   }
 
  private:
-  char* _writePtr;
-  char* _startPtr;
+  char* writePtr_;
+  char* startPtr_;
 };
-}  // namespace ARDUINOJSON_NAMESPACE
+
+ARDUINOJSON_END_PRIVATE_NAMESPACE
diff --git a/neosensor/libraries/ArduinoJson/src/ArduinoJson/StringStorage/StringStorage.hpp b/neosensor/libraries/ArduinoJson/src/ArduinoJson/StringStorage/StringStorage.hpp
index 8ef034be..0b1f77cc 100644
--- a/neosensor/libraries/ArduinoJson/src/ArduinoJson/StringStorage/StringStorage.hpp
+++ b/neosensor/libraries/ArduinoJson/src/ArduinoJson/StringStorage/StringStorage.hpp
@@ -1,5 +1,5 @@
 // ArduinoJson - https://arduinojson.org
-// Copyright © 2014-2022, Benoit BLANCHON
+// Copyright © 2014-2023, Benoit BLANCHON
 // MIT License
 
 #pragma once
@@ -7,7 +7,7 @@
 #include <ArduinoJson/StringStorage/StringCopier.hpp>
 #include <ArduinoJson/StringStorage/StringMover.hpp>
 
-namespace ARDUINOJSON_NAMESPACE {
+ARDUINOJSON_BEGIN_PRIVATE_NAMESPACE
 
 template <typename TInput>
 StringCopier makeStringStorage(TInput&, MemoryPool* pool) {
@@ -21,4 +21,4 @@ StringMover makeStringStorage(
     typename enable_if<!is_const<TChar>::value>::type* = 0) {
   return StringMover(reinterpret_cast<char*>(input));
 }
-}  // namespace ARDUINOJSON_NAMESPACE
+ARDUINOJSON_END_PRIVATE_NAMESPACE
diff --git a/neosensor/libraries/ArduinoJson/src/ArduinoJson/Strings/Adapters/ArduinoString.hpp b/neosensor/libraries/ArduinoJson/src/ArduinoJson/Strings/Adapters/ArduinoString.hpp
deleted file mode 100644
index 13d39181..00000000
--- a/neosensor/libraries/ArduinoJson/src/ArduinoJson/Strings/Adapters/ArduinoString.hpp
+++ /dev/null
@@ -1,25 +0,0 @@
-// ArduinoJson - https://arduinojson.org
-// Copyright © 2014-2022, Benoit BLANCHON
-// MIT License
-
-#pragma once
-
-#include <Arduino.h>
-
-#include <ArduinoJson/Strings/Adapters/RamString.hpp>
-#include <ArduinoJson/Strings/StringAdapter.hpp>
-
-namespace ARDUINOJSON_NAMESPACE {
-
-template <typename T>
-struct StringAdapter<
-    T, typename enable_if<is_same<T, ::String>::value ||
-                          is_same<T, ::StringSumHelper>::value>::type> {
-  typedef SizedRamString AdaptedString;
-
-  static AdaptedString adapt(const ::String& s) {
-    return AdaptedString(s.c_str(), s.length());
-  }
-};
-
-}  // namespace ARDUINOJSON_NAMESPACE
diff --git a/neosensor/libraries/ArduinoJson/src/ArduinoJson/Strings/Adapters/FlashString.hpp b/neosensor/libraries/ArduinoJson/src/ArduinoJson/Strings/Adapters/FlashString.hpp
index ccd07bfa..4989315a 100644
--- a/neosensor/libraries/ArduinoJson/src/ArduinoJson/Strings/Adapters/FlashString.hpp
+++ b/neosensor/libraries/ArduinoJson/src/ArduinoJson/Strings/Adapters/FlashString.hpp
@@ -1,35 +1,33 @@
 // ArduinoJson - https://arduinojson.org
-// Copyright © 2014-2022, Benoit BLANCHON
+// Copyright © 2014-2023, Benoit BLANCHON
 // MIT License
 
 #pragma once
 
-#include <Arduino.h>
-
 #include <ArduinoJson/Polyfills/pgmspace.hpp>
 #include <ArduinoJson/Strings/StringAdapter.hpp>
 
-namespace ARDUINOJSON_NAMESPACE {
+ARDUINOJSON_BEGIN_PRIVATE_NAMESPACE
 
 class FlashString {
  public:
   static const size_t typeSortKey = 1;
 
   FlashString(const __FlashStringHelper* str, size_t sz)
-      : _str(reinterpret_cast<const char*>(str)), _size(sz) {}
+      : str_(reinterpret_cast<const char*>(str)), size_(sz) {}
 
   bool isNull() const {
-    return !_str;
+    return !str_;
   }
 
   char operator[](size_t i) const {
-    ARDUINOJSON_ASSERT(_str != 0);
-    ARDUINOJSON_ASSERT(i <= _size);
-    return static_cast<char>(pgm_read_byte(_str + i));
+    ARDUINOJSON_ASSERT(str_ != 0);
+    ARDUINOJSON_ASSERT(i <= size_);
+    return static_cast<char>(pgm_read_byte(str_ + i));
   }
 
   size_t size() const {
-    return _size;
+    return size_;
   }
 
   friend bool stringEquals(FlashString a, SizedRamString b) {
@@ -38,7 +36,7 @@ class FlashString {
     ARDUINOJSON_ASSERT(!b.isNull());
     if (a.size() != b.size())
       return false;
-    return ::memcmp_P(b.data(), a._str, a._size) == 0;
+    return ::memcmp_P(b.data(), a.str_, a.size_) == 0;
   }
 
   friend int stringCompare(FlashString a, SizedRamString b) {
@@ -46,7 +44,7 @@ class FlashString {
     ARDUINOJSON_ASSERT(!a.isNull());
     ARDUINOJSON_ASSERT(!b.isNull());
     size_t minsize = a.size() < b.size() ? a.size() : b.size();
-    int res = ::memcmp_P(b.data(), a._str, minsize);
+    int res = ::memcmp_P(b.data(), a.str_, minsize);
     if (res)
       return -res;
     if (a.size() < b.size())
@@ -58,7 +56,7 @@ class FlashString {
 
   friend void stringGetChars(FlashString s, char* p, size_t n) {
     ARDUINOJSON_ASSERT(s.size() <= n);
-    ::memcpy_P(p, s._str, n);
+    ::memcpy_P(p, s.str_, n);
   }
 
   StringStoragePolicy::Copy storagePolicy() const {
@@ -66,8 +64,8 @@ class FlashString {
   }
 
  private:
-  const char* _str;
-  size_t _size;
+  const char* str_;
+  size_t size_;
 };
 
 template <>
@@ -88,4 +86,4 @@ struct SizedStringAdapter<const __FlashStringHelper*, void> {
   }
 };
 
-}  // namespace ARDUINOJSON_NAMESPACE
+ARDUINOJSON_END_PRIVATE_NAMESPACE
diff --git a/neosensor/libraries/ArduinoJson/src/ArduinoJson/Strings/Adapters/JsonString.hpp b/neosensor/libraries/ArduinoJson/src/ArduinoJson/Strings/Adapters/JsonString.hpp
index eb203ba2..5db7c208 100644
--- a/neosensor/libraries/ArduinoJson/src/ArduinoJson/Strings/Adapters/JsonString.hpp
+++ b/neosensor/libraries/ArduinoJson/src/ArduinoJson/Strings/Adapters/JsonString.hpp
@@ -1,5 +1,5 @@
 // ArduinoJson - https://arduinojson.org
-// Copyright © 2014-2022, Benoit BLANCHON
+// Copyright © 2014-2023, Benoit BLANCHON
 // MIT License
 
 #pragma once
@@ -8,20 +8,20 @@
 #include <ArduinoJson/Strings/JsonString.hpp>
 #include <ArduinoJson/Strings/StringAdapter.hpp>
 
-namespace ARDUINOJSON_NAMESPACE {
+ARDUINOJSON_BEGIN_PRIVATE_NAMESPACE
 
 class JsonStringAdapter : public SizedRamString {
  public:
   JsonStringAdapter(const JsonString& s)
-      : SizedRamString(s.c_str(), s.size()), _linked(s.isLinked()) {}
+      : SizedRamString(s.c_str(), s.size()), linked_(s.isLinked()) {}
 
   StringStoragePolicy::LinkOrCopy storagePolicy() const {
-    StringStoragePolicy::LinkOrCopy policy = {_linked};
+    StringStoragePolicy::LinkOrCopy policy = {linked_};
     return policy;
   }
 
  private:
-  bool _linked;
+  bool linked_;
 };
 
 template <>
@@ -33,4 +33,4 @@ struct StringAdapter<JsonString> {
   }
 };
 
-}  // namespace ARDUINOJSON_NAMESPACE
+ARDUINOJSON_END_PRIVATE_NAMESPACE
diff --git a/neosensor/libraries/ArduinoJson/src/ArduinoJson/Strings/Adapters/RamString.hpp b/neosensor/libraries/ArduinoJson/src/ArduinoJson/Strings/Adapters/RamString.hpp
index 164478ef..12c67c94 100644
--- a/neosensor/libraries/ArduinoJson/src/ArduinoJson/Strings/Adapters/RamString.hpp
+++ b/neosensor/libraries/ArduinoJson/src/ArduinoJson/Strings/Adapters/RamString.hpp
@@ -1,5 +1,5 @@
 // ArduinoJson - https://arduinojson.org
-// Copyright © 2014-2022, Benoit BLANCHON
+// Copyright © 2014-2023, Benoit BLANCHON
 // MIT License
 
 #pragma once
@@ -11,7 +11,7 @@
 #include <ArduinoJson/Strings/StoragePolicy.hpp>
 #include <ArduinoJson/Strings/StringAdapter.hpp>
 
-namespace ARDUINOJSON_NAMESPACE {
+ARDUINOJSON_BEGIN_PRIVATE_NAMESPACE
 
 template <typename T>
 struct IsChar
@@ -21,31 +21,31 @@ class ZeroTerminatedRamString {
  public:
   static const size_t typeSortKey = 3;
 
-  ZeroTerminatedRamString(const char* str) : _str(str) {}
+  ZeroTerminatedRamString(const char* str) : str_(str) {}
 
   bool isNull() const {
-    return !_str;
+    return !str_;
   }
 
   size_t size() const {
-    return _str ? ::strlen(_str) : 0;
+    return str_ ? ::strlen(str_) : 0;
   }
 
   char operator[](size_t i) const {
-    ARDUINOJSON_ASSERT(_str != 0);
+    ARDUINOJSON_ASSERT(str_ != 0);
     ARDUINOJSON_ASSERT(i <= size());
-    return _str[i];
+    return str_[i];
   }
 
   const char* data() const {
-    return _str;
+    return str_;
   }
 
   friend int stringCompare(ZeroTerminatedRamString a,
                            ZeroTerminatedRamString b) {
     ARDUINOJSON_ASSERT(!a.isNull());
     ARDUINOJSON_ASSERT(!b.isNull());
-    return ::strcmp(a._str, b._str);
+    return ::strcmp(a.str_, b.str_);
   }
 
   friend bool stringEquals(ZeroTerminatedRamString a,
@@ -58,7 +58,7 @@ class ZeroTerminatedRamString {
   }
 
  protected:
-  const char* _str;
+  const char* str_;
 };
 
 template <typename TChar>
@@ -101,24 +101,24 @@ class SizedRamString {
  public:
   static const size_t typeSortKey = 2;
 
-  SizedRamString(const char* str, size_t sz) : _str(str), _size(sz) {}
+  SizedRamString(const char* str, size_t sz) : str_(str), size_(sz) {}
 
   bool isNull() const {
-    return !_str;
+    return !str_;
   }
 
   size_t size() const {
-    return _size;
+    return size_;
   }
 
   char operator[](size_t i) const {
-    ARDUINOJSON_ASSERT(_str != 0);
+    ARDUINOJSON_ASSERT(str_ != 0);
     ARDUINOJSON_ASSERT(i <= size());
-    return _str[i];
+    return str_[i];
   }
 
   const char* data() const {
-    return _str;
+    return str_;
   }
 
   StringStoragePolicy::Copy storagePolicy() const {
@@ -126,8 +126,8 @@ class SizedRamString {
   }
 
  protected:
-  const char* _str;
-  size_t _size;
+  const char* str_;
+  size_t size_;
 };
 
 template <typename TChar>
@@ -140,4 +140,4 @@ struct SizedStringAdapter<TChar*,
   }
 };
 
-}  // namespace ARDUINOJSON_NAMESPACE
+ARDUINOJSON_END_PRIVATE_NAMESPACE
diff --git a/neosensor/libraries/ArduinoJson/src/ArduinoJson/Strings/Adapters/StdString.hpp b/neosensor/libraries/ArduinoJson/src/ArduinoJson/Strings/Adapters/StdString.hpp
deleted file mode 100644
index 76f39fa8..00000000
--- a/neosensor/libraries/ArduinoJson/src/ArduinoJson/Strings/Adapters/StdString.hpp
+++ /dev/null
@@ -1,23 +0,0 @@
-// ArduinoJson - https://arduinojson.org
-// Copyright © 2014-2022, Benoit BLANCHON
-// MIT License
-
-#pragma once
-
-#include <ArduinoJson/Strings/Adapters/RamString.hpp>
-
-#include <string>
-
-namespace ARDUINOJSON_NAMESPACE {
-
-template <typename TCharTraits, typename TAllocator>
-struct StringAdapter<std::basic_string<char, TCharTraits, TAllocator>, void> {
-  typedef SizedRamString AdaptedString;
-
-  static AdaptedString adapt(
-      const std::basic_string<char, TCharTraits, TAllocator>& s) {
-    return AdaptedString(s.c_str(), s.size());
-  }
-};
-
-}  // namespace ARDUINOJSON_NAMESPACE
diff --git a/neosensor/libraries/ArduinoJson/src/ArduinoJson/Strings/Adapters/StringObject.hpp b/neosensor/libraries/ArduinoJson/src/ArduinoJson/Strings/Adapters/StringObject.hpp
new file mode 100644
index 00000000..d52b0c7f
--- /dev/null
+++ b/neosensor/libraries/ArduinoJson/src/ArduinoJson/Strings/Adapters/StringObject.hpp
@@ -0,0 +1,51 @@
+// ArduinoJson - https://arduinojson.org
+// Copyright © 2014-2023, Benoit BLANCHON
+// MIT License
+
+#pragma once
+
+#include <ArduinoJson/Strings/Adapters/RamString.hpp>
+#include <ArduinoJson/Strings/StringAdapter.hpp>
+#include <ArduinoJson/Strings/StringTraits.hpp>
+
+ARDUINOJSON_BEGIN_PRIVATE_NAMESPACE
+
+template <typename T>
+struct StringAdapter<
+    T,
+    typename enable_if<
+        (string_traits<T>::has_cstr || string_traits<T>::has_data) &&
+        (string_traits<T>::has_length || string_traits<T>::has_size)>::type> {
+  typedef SizedRamString AdaptedString;
+
+  static AdaptedString adapt(const T& s) {
+    return AdaptedString(get_data(s), get_size(s));
+  }
+
+ private:
+  template <typename U>
+  static typename enable_if<string_traits<U>::has_size, size_t>::type get_size(
+      const U& s) {
+    return s.size();
+  }
+
+  template <typename U>
+  static typename enable_if<!string_traits<U>::has_size, size_t>::type get_size(
+      const U& s) {
+    return s.length();
+  }
+
+  template <typename U>
+  static typename enable_if<string_traits<U>::has_data, const char*>::type
+  get_data(const U& s) {
+    return s.data();
+  }
+
+  template <typename U>
+  static typename enable_if<!string_traits<U>::has_data, const char*>::type
+  get_data(const U& s) {
+    return s.c_str();
+  }
+};
+
+ARDUINOJSON_END_PRIVATE_NAMESPACE
diff --git a/neosensor/libraries/ArduinoJson/src/ArduinoJson/Strings/Adapters/StringView.hpp b/neosensor/libraries/ArduinoJson/src/ArduinoJson/Strings/Adapters/StringView.hpp
deleted file mode 100644
index 543f7658..00000000
--- a/neosensor/libraries/ArduinoJson/src/ArduinoJson/Strings/Adapters/StringView.hpp
+++ /dev/null
@@ -1,22 +0,0 @@
-// ArduinoJson - https://arduinojson.org
-// Copyright © 2014-2022, Benoit BLANCHON
-// MIT License
-
-#pragma once
-
-#include <ArduinoJson/Strings/Adapters/RamString.hpp>
-
-#include <string_view>
-
-namespace ARDUINOJSON_NAMESPACE {
-
-template <>
-struct StringAdapter<std::string_view, void> {
-  typedef SizedRamString AdaptedString;
-
-  static AdaptedString adapt(const std::string_view& s) {
-    return AdaptedString(s.data(), s.size());
-  }
-};
-
-}  // namespace ARDUINOJSON_NAMESPACE
diff --git a/neosensor/libraries/ArduinoJson/src/ArduinoJson/Strings/IsString.hpp b/neosensor/libraries/ArduinoJson/src/ArduinoJson/Strings/IsString.hpp
index e674a64e..31e4601d 100644
--- a/neosensor/libraries/ArduinoJson/src/ArduinoJson/Strings/IsString.hpp
+++ b/neosensor/libraries/ArduinoJson/src/ArduinoJson/Strings/IsString.hpp
@@ -1,5 +1,5 @@
 // ArduinoJson - https://arduinojson.org
-// Copyright © 2014-2022, Benoit BLANCHON
+// Copyright © 2014-2023, Benoit BLANCHON
 // MIT License
 
 #pragma once
@@ -7,7 +7,7 @@
 #include <ArduinoJson/Polyfills/type_traits.hpp>
 #include <ArduinoJson/Strings/StringAdapter.hpp>
 
-namespace ARDUINOJSON_NAMESPACE {
+ARDUINOJSON_BEGIN_PRIVATE_NAMESPACE
 
 template <typename T, typename Enable = void>
 struct IsString : false_type {};
@@ -17,4 +17,4 @@ struct IsString<
     T, typename make_void<typename StringAdapter<T>::AdaptedString>::type>
     : true_type {};
 
-}  // namespace ARDUINOJSON_NAMESPACE
+ARDUINOJSON_END_PRIVATE_NAMESPACE
diff --git a/neosensor/libraries/ArduinoJson/src/ArduinoJson/Strings/JsonString.hpp b/neosensor/libraries/ArduinoJson/src/ArduinoJson/Strings/JsonString.hpp
index 3c4cc0b4..538dbc8a 100644
--- a/neosensor/libraries/ArduinoJson/src/ArduinoJson/Strings/JsonString.hpp
+++ b/neosensor/libraries/ArduinoJson/src/ArduinoJson/Strings/JsonString.hpp
@@ -1,68 +1,66 @@
 // ArduinoJson - https://arduinojson.org
-// Copyright © 2014-2022, Benoit BLANCHON
+// Copyright © 2014-2023, Benoit BLANCHON
 // MIT License
 
 #pragma once
 
-#include <ArduinoJson/Misc/SafeBoolIdiom.hpp>
-
 #if ARDUINOJSON_ENABLE_STD_STREAM
 #  include <ostream>
 #endif
 
-namespace ARDUINOJSON_NAMESPACE {
+ARDUINOJSON_BEGIN_PUBLIC_NAMESPACE
 
 // A string.
 // https://arduinojson.org/v6/api/jsonstring/
-class JsonString : public SafeBoolIdom<JsonString> {
+class JsonString {
  public:
   enum Ownership { Copied, Linked };
 
-  JsonString() : _data(0), _size(0), _ownership(Linked) {}
+  JsonString() : data_(0), size_(0), ownership_(Linked) {}
 
   JsonString(const char* data, Ownership ownership = Linked)
-      : _data(data), _size(data ? ::strlen(data) : 0), _ownership(ownership) {}
+      : data_(data), size_(data ? ::strlen(data) : 0), ownership_(ownership) {}
 
   JsonString(const char* data, size_t sz, Ownership ownership = Linked)
-      : _data(data), _size(sz), _ownership(ownership) {}
+      : data_(data), size_(sz), ownership_(ownership) {}
 
   // Returns a pointer to the characters.
   const char* c_str() const {
-    return _data;
+    return data_;
   }
 
   // Returns true if the string is null.
   bool isNull() const {
-    return !_data;
+    return !data_;
   }
 
   // Returns true if the string is stored by address.
   // Returns false if the string is stored by copy.
   bool isLinked() const {
-    return _ownership == Linked;
+    return ownership_ == Linked;
   }
 
   // Returns length of the string.
   size_t size() const {
-    return _size;
+    return size_;
   }
 
-  // safe bool idiom
-  operator bool_type() const {
-    return _data ? safe_true() : safe_false();
+  // Returns true if the string is non-null
+  explicit operator bool() const {
+    return data_ != 0;
   }
 
   // Returns true if strings are equal.
   friend bool operator==(JsonString lhs, JsonString rhs) {
-    if (lhs._size != rhs._size)
+    if (lhs.size_ != rhs.size_)
       return false;
-    if (lhs._data == rhs._data)
+    if (lhs.data_ == rhs.data_)
       return true;
-    if (!lhs._data)
+    if (!lhs.data_)
       return false;
-    if (!rhs._data)
+    if (!rhs.data_)
       return false;
-    return memcmp(lhs._data, rhs._data, lhs._size) == 0;
+    return memcmp(lhs.data_, rhs.data_, lhs.size_) == 0;
   }
 
   // Returns true if strings differs.
@@ -78,9 +76,9 @@ class JsonString : public SafeBoolIdom<JsonString> {
 #endif
 
  private:
-  const char* _data;
-  size_t _size;
-  Ownership _ownership;
+  const char* data_;
+  size_t size_;
+  Ownership ownership_;
 };
 
-}  // namespace ARDUINOJSON_NAMESPACE
+ARDUINOJSON_END_PUBLIC_NAMESPACE
diff --git a/neosensor/libraries/ArduinoJson/src/ArduinoJson/Strings/StoragePolicy.hpp b/neosensor/libraries/ArduinoJson/src/ArduinoJson/Strings/StoragePolicy.hpp
index 8f3d2730..b8b3b6a6 100644
--- a/neosensor/libraries/ArduinoJson/src/ArduinoJson/Strings/StoragePolicy.hpp
+++ b/neosensor/libraries/ArduinoJson/src/ArduinoJson/Strings/StoragePolicy.hpp
@@ -1,10 +1,10 @@
 // ArduinoJson - https://arduinojson.org
-// Copyright © 2014-2022, Benoit BLANCHON
+// Copyright © 2014-2023, Benoit BLANCHON
 // MIT License
 
 #pragma once
 
-namespace ARDUINOJSON_NAMESPACE {
+ARDUINOJSON_BEGIN_PRIVATE_NAMESPACE
 
 namespace StringStoragePolicy {
 
@@ -15,4 +15,4 @@ struct LinkOrCopy {
 };
 }  // namespace StringStoragePolicy
 
-}  // namespace ARDUINOJSON_NAMESPACE
+ARDUINOJSON_END_PRIVATE_NAMESPACE
diff --git a/neosensor/libraries/ArduinoJson/src/ArduinoJson/Strings/StringAdapter.hpp b/neosensor/libraries/ArduinoJson/src/ArduinoJson/Strings/StringAdapter.hpp
index 0f19d260..7ebad0b5 100644
--- a/neosensor/libraries/ArduinoJson/src/ArduinoJson/Strings/StringAdapter.hpp
+++ b/neosensor/libraries/ArduinoJson/src/ArduinoJson/Strings/StringAdapter.hpp
@@ -1,10 +1,10 @@
 // ArduinoJson - https://arduinojson.org
-// Copyright © 2014-2022, Benoit BLANCHON
+// Copyright © 2014-2023, Benoit BLANCHON
 // MIT License
 
 #pragma once
 
-namespace ARDUINOJSON_NAMESPACE {
+ARDUINOJSON_BEGIN_PRIVATE_NAMESPACE
 
 template <typename TString, typename Enable = void>
 struct StringAdapter;
@@ -28,4 +28,4 @@ typename SizedStringAdapter<TChar*>::AdaptedString adaptString(TChar* p,
   return SizedStringAdapter<TChar*>::adapt(p, n);
 }
 
-}  // namespace ARDUINOJSON_NAMESPACE
+ARDUINOJSON_END_PRIVATE_NAMESPACE
diff --git a/neosensor/libraries/ArduinoJson/src/ArduinoJson/Strings/StringAdapters.hpp b/neosensor/libraries/ArduinoJson/src/ArduinoJson/Strings/StringAdapters.hpp
index 0be5cd90..edf881ce 100644
--- a/neosensor/libraries/ArduinoJson/src/ArduinoJson/Strings/StringAdapters.hpp
+++ b/neosensor/libraries/ArduinoJson/src/ArduinoJson/Strings/StringAdapters.hpp
@@ -1,5 +1,5 @@
 // ArduinoJson - https://arduinojson.org
-// Copyright © 2014-2022, Benoit BLANCHON
+// Copyright © 2014-2023, Benoit BLANCHON
 // MIT License
 
 #pragma once
@@ -7,24 +7,13 @@
 #include <ArduinoJson/Polyfills/type_traits.hpp>
 #include <ArduinoJson/Strings/Adapters/JsonString.hpp>
 #include <ArduinoJson/Strings/Adapters/RamString.hpp>
-
-#if ARDUINOJSON_ENABLE_STD_STRING
-#  include <ArduinoJson/Strings/Adapters/StdString.hpp>
-#endif
-
-#if ARDUINOJSON_ENABLE_STRING_VIEW
-#  include <ArduinoJson/Strings/Adapters/StringView.hpp>
-#endif
-
-#if ARDUINOJSON_ENABLE_ARDUINO_STRING
-#  include <ArduinoJson/Strings/Adapters/ArduinoString.hpp>
-#endif
+#include <ArduinoJson/Strings/Adapters/StringObject.hpp>
 
 #if ARDUINOJSON_ENABLE_PROGMEM
 #  include <ArduinoJson/Strings/Adapters/FlashString.hpp>
 #endif
 
-namespace ARDUINOJSON_NAMESPACE {
+ARDUINOJSON_BEGIN_PRIVATE_NAMESPACE
 
 template <typename TAdaptedString1, typename TAdaptedString2>
 typename enable_if<TAdaptedString1::typeSortKey <= TAdaptedString2::typeSortKey,
@@ -85,4 +74,4 @@ static void stringGetChars(TAdaptedString s, char* p, size_t n) {
   }
 }
 
-}  // namespace ARDUINOJSON_NAMESPACE
+ARDUINOJSON_END_PRIVATE_NAMESPACE
diff --git a/neosensor/libraries/ArduinoJson/src/ArduinoJson/Strings/StringTraits.hpp b/neosensor/libraries/ArduinoJson/src/ArduinoJson/Strings/StringTraits.hpp
new file mode 100644
index 00000000..45804cd1
--- /dev/null
+++ b/neosensor/libraries/ArduinoJson/src/ArduinoJson/Strings/StringTraits.hpp
@@ -0,0 +1,79 @@
+// ArduinoJson - https://arduinojson.org
+// Copyright © 2014-2023, Benoit BLANCHON
+// MIT License
+
+#pragma once
+
+#include <ArduinoJson/Strings/Adapters/RamString.hpp>
+#include <ArduinoJson/Strings/StringAdapter.hpp>
+
+ARDUINOJSON_BEGIN_PRIVATE_NAMESPACE
+
+namespace string_traits_impl {
+
+// const char* c_str() const
+// - String
+// - std::string
+
+template <class T, class = void>
+struct has_cstr : false_type {};
+
+template <class T>
+struct has_cstr<T,
+                typename enable_if<is_same<decltype(declval<const T>().c_str()),
+                                           const char*>::value>::type>
+    : true_type {};
+
+// const char* data() const
+// - std::string
+// - std::string_view
+// - etl::string
+
+template <class T, class = void>
+struct has_data : false_type {};
+
+template <class T>
+struct has_data<T,
+                typename enable_if<is_same<decltype(declval<const T>().data()),
+                                           const char*>::value>::type>
+    : true_type {};
+
+// size_t length() const
+// - String
+
+template <class T, class = void>
+struct has_length : false_type {};
+
+template <class T>
+struct has_length<
+    T, typename enable_if<
+           is_same<decltype(declval<const T>().length()), size_t>::value>::type>
+    : true_type {};
+
+// size_t size() const
+// - std::string
+// - std::string_view
+// - etl::string
+
+template <class T, class = void>
+struct has_size : false_type {};
+
+template <class T>
+struct has_size<
+    T, typename enable_if<
+           is_same<decltype(declval<const T>().size()), size_t>::value>::type>
+    : true_type {};
+
+}  // namespace string_traits_impl
+
+template <typename T>
+struct string_traits {
+  enum {
+    has_cstr = string_traits_impl::has_cstr<T>::value,
+    has_length = string_traits_impl::has_length<T>::value,
+    has_data = string_traits_impl::has_data<T>::value,
+    has_size = string_traits_impl::has_size<T>::value
+  };
+};
+
+ARDUINOJSON_END_PRIVATE_NAMESPACE
diff --git a/neosensor/libraries/ArduinoJson/src/ArduinoJson/Variant/Converter.hpp b/neosensor/libraries/ArduinoJson/src/ArduinoJson/Variant/Converter.hpp
index 63604dc9..8a63d996 100644
--- a/neosensor/libraries/ArduinoJson/src/ArduinoJson/Variant/Converter.hpp
+++ b/neosensor/libraries/ArduinoJson/src/ArduinoJson/Variant/Converter.hpp
@@ -1,16 +1,20 @@
 // ArduinoJson - https://arduinojson.org
-// Copyright © 2014-2022, Benoit BLANCHON
+// Copyright © 2014-2023, Benoit BLANCHON
 // MIT License
 
 #pragma once
 
 #include <ArduinoJson/Namespace.hpp>
 
-namespace ARDUINOJSON_NAMESPACE {
+ARDUINOJSON_BEGIN_PUBLIC_NAMESPACE
 
 template <typename T, typename Enable = void>
 struct Converter;
 
+ARDUINOJSON_END_PUBLIC_NAMESPACE
+
+ARDUINOJSON_BEGIN_PRIVATE_NAMESPACE
+
 // clang-format off
 template <typename T1, typename T2>
 class InvalidConversion;  // Error here? See https://arduinojson.org/v6/invalid-conversion/
@@ -19,4 +23,4 @@ class InvalidConversion;  // Error here? See https://arduinojson.org/v6/invalid-
 template <typename T>
 struct ConverterNeedsWriteableRef;
 
-}  // namespace ARDUINOJSON_NAMESPACE
+ARDUINOJSON_END_PRIVATE_NAMESPACE
diff --git a/neosensor/libraries/ArduinoJson/src/ArduinoJson/Variant/ConverterImpl.hpp b/neosensor/libraries/ArduinoJson/src/ArduinoJson/Variant/ConverterImpl.hpp
index f74bcfee..e95b33e5 100644
--- a/neosensor/libraries/ArduinoJson/src/ArduinoJson/Variant/ConverterImpl.hpp
+++ b/neosensor/libraries/ArduinoJson/src/ArduinoJson/Variant/ConverterImpl.hpp
@@ -1,5 +1,5 @@
 // ArduinoJson - https://arduinojson.org
-// Copyright © 2014-2022, Benoit BLANCHON
+// Copyright © 2014-2023, Benoit BLANCHON
 // MIT License
 
 #pragma once
@@ -8,7 +8,7 @@
 #include <ArduinoJson/Variant/JsonVariantConst.hpp>
 #include <ArduinoJson/Variant/VariantFunctions.hpp>
 
-namespace ARDUINOJSON_NAMESPACE {
+ARDUINOJSON_BEGIN_PUBLIC_NAMESPACE
 
 template <typename T, typename Enable>
 struct Converter {
@@ -36,11 +36,12 @@ struct Converter {
 
 template <typename T>
 struct Converter<
-    T, typename enable_if<is_integral<T>::value && !is_same<bool, T>::value &&
-                          !is_same<char, T>::value>::type>
-    : private VariantAttorney {
+    T, typename detail::enable_if<detail::is_integral<T>::value &&
+                                  !detail::is_same<bool, T>::value &&
+                                  !detail::is_same<char, T>::value>::type>
+    : private detail::VariantAttorney {
   static void toJson(T src, JsonVariant dst) {
-    VariantData* data = getData(dst);
+    auto data = getData(dst);
     ARDUINOJSON_ASSERT_INTEGER_TYPE_IS_SUPPORTED(T);
     if (data)
       data->setInteger(src);
@@ -48,119 +49,122 @@ struct Converter<
 
   static T fromJson(JsonVariantConst src) {
     ARDUINOJSON_ASSERT_INTEGER_TYPE_IS_SUPPORTED(T);
-    const VariantData* data = getData(src);
-    return data ? data->asIntegral<T>() : T();
+    auto data = getData(src);
+    return data ? data->template asIntegral<T>() : T();
   }
 
   static bool checkJson(JsonVariantConst src) {
-    const VariantData* data = getData(src);
-    return data && data->isInteger<T>();
+    auto data = getData(src);
+    return data && data->template isInteger<T>();
   }
 };
 
 template <typename T>
-struct Converter<T, typename enable_if<is_enum<T>::value>::type>
-    : private VariantAttorney {
+struct Converter<T, typename detail::enable_if<detail::is_enum<T>::value>::type>
+    : private detail::VariantAttorney {
   static void toJson(T src, JsonVariant dst) {
     dst.set(static_cast<JsonInteger>(src));
   }
 
   static T fromJson(JsonVariantConst src) {
-    const VariantData* data = getData(src);
-    return data ? static_cast<T>(data->asIntegral<int>()) : T();
+    auto data = getData(src);
+    return data ? static_cast<T>(data->template asIntegral<int>()) : T();
   }
 
   static bool checkJson(JsonVariantConst src) {
-    const VariantData* data = getData(src);
-    return data && data->isInteger<int>();
+    auto data = getData(src);
+    return data && data->template isInteger<int>();
   }
 };
 
 template <>
-struct Converter<bool> : private VariantAttorney {
+struct Converter<bool> : private detail::VariantAttorney {
   static void toJson(bool src, JsonVariant dst) {
-    VariantData* data = getData(dst);
+    auto data = getData(dst);
     if (data)
       data->setBoolean(src);
   }
 
   static bool fromJson(JsonVariantConst src) {
-    const VariantData* data = getData(src);
+    auto data = getData(src);
     return data ? data->asBoolean() : false;
   }
 
   static bool checkJson(JsonVariantConst src) {
-    const VariantData* data = getData(src);
+    auto data = getData(src);
     return data && data->isBoolean();
   }
 };
 
 template <typename T>
-struct Converter<T, typename enable_if<is_floating_point<T>::value>::type>
-    : private VariantAttorney {
+struct Converter<
+    T, typename detail::enable_if<detail::is_floating_point<T>::value>::type>
+    : private detail::VariantAttorney {
   static void toJson(T src, JsonVariant dst) {
-    VariantData* data = getData(dst);
+    auto data = getData(dst);
     if (data)
       data->setFloat(static_cast<JsonFloat>(src));
   }
 
   static T fromJson(JsonVariantConst src) {
-    const VariantData* data = getData(src);
-    return data ? data->asFloat<T>() : 0;
+    auto data = getData(src);
+    return data ? data->template asFloat<T>() : 0;
   }
 
   static bool checkJson(JsonVariantConst src) {
-    const VariantData* data = getData(src);
+    auto data = getData(src);
     return data && data->isFloat();
   }
 };
 
 template <>
-struct Converter<const char*> : private VariantAttorney {
+struct Converter<const char*> : private detail::VariantAttorney {
   static void toJson(const char* src, JsonVariant dst) {
-    variantSetString(getData(dst), adaptString(src), getPool(dst));
+    variantSetString(getData(dst), detail::adaptString(src), getPool(dst));
   }
 
   static const char* fromJson(JsonVariantConst src) {
-    const VariantData* data = getData(src);
+    auto data = getData(src);
     return data ? data->asString().c_str() : 0;
   }
 
   static bool checkJson(JsonVariantConst src) {
-    const VariantData* data = getData(src);
+    auto data = getData(src);
     return data && data->isString();
   }
 };
 
 template <>
-struct Converter<JsonString> : private VariantAttorney {
+struct Converter<JsonString> : private detail::VariantAttorney {
   static void toJson(JsonString src, JsonVariant dst) {
-    variantSetString(getData(dst), adaptString(src), getPool(dst));
+    variantSetString(getData(dst), detail::adaptString(src), getPool(dst));
   }
 
   static JsonString fromJson(JsonVariantConst src) {
-    const VariantData* data = getData(src);
+    auto data = getData(src);
     return data ? data->asString() : 0;
   }
 
   static bool checkJson(JsonVariantConst src) {
-    const VariantData* data = getData(src);
+    auto data = getData(src);
     return data && data->isString();
   }
 };
 
 template <typename T>
-inline typename enable_if<IsString<T>::value, bool>::type convertToJson(
-    const T& src, JsonVariant dst) {
-  VariantData* data = VariantAttorney::getData(dst);
-  MemoryPool* pool = VariantAttorney::getPool(dst);
+inline typename detail::enable_if<detail::IsString<T>::value, bool>::type
+convertToJson(const T& src, JsonVariant dst) {
+  using namespace detail;
+  auto data = VariantAttorney::getData(dst);
+  auto pool = VariantAttorney::getPool(dst);
   return variantSetString(data, adaptString(src), pool);
 }
 
 template <>
-struct Converter<SerializedValue<const char*> > {
+struct Converter<SerializedValue<const char*>>
+    : private detail::VariantAttorney {
   static void toJson(SerializedValue<const char*> src, JsonVariant dst) {
-    VariantData* data = VariantAttorney::getData(dst);
+    auto data = getData(dst);
     if (data)
       data->setLinkedRaw(src);
   }
@@ -170,21 +174,20 @@ struct Converter<SerializedValue<const char*> > {
 // SerializedValue<String>
 // SerializedValue<const __FlashStringHelper*>
 template <typename T>
-struct Converter<SerializedValue<T>,
-                 typename enable_if<!is_same<const char*, T>::value>::type>
-    : private VariantAttorney {
+struct Converter<
+    SerializedValue<T>,
+    typename detail::enable_if<!detail::is_same<const char*, T>::value>::type>
+    : private detail::VariantAttorney {
   static void toJson(SerializedValue<T> src, JsonVariant dst) {
-    VariantData* data = getData(dst);
-    MemoryPool* pool = getPool(dst);
+    auto data = getData(dst);
+    auto pool = getPool(dst);
     if (data)
       data->storeOwnedRaw(src, pool);
   }
 };
 
-#if ARDUINOJSON_HAS_NULLPTR
-
 template <>
-struct Converter<decltype(nullptr)> : private VariantAttorney {
+struct Converter<decltype(nullptr)> : private detail::VariantAttorney {
   static void toJson(decltype(nullptr), JsonVariant dst) {
     variantSetNull(getData(dst));
   }
@@ -192,62 +195,62 @@ struct Converter<decltype(nullptr)> : private VariantAttorney {
     return nullptr;
   }
   static bool checkJson(JsonVariantConst src) {
-    const VariantData* data = getData(src);
+    auto data = getData(src);
     return data == 0 || data->isNull();
   }
 };
 
-#endif
-
 #if ARDUINOJSON_ENABLE_ARDUINO_STREAM
 
+namespace detail {
 class MemoryPoolPrint : public Print {
  public:
-  MemoryPoolPrint(MemoryPool* pool) : _pool(pool), _size(0) {
-    pool->getFreeZone(&_string, &_capacity);
+  MemoryPoolPrint(MemoryPool* pool) : pool_(pool), size_(0) {
+    pool->getFreeZone(&string_, &capacity_);
   }
 
   JsonString str() {
-    ARDUINOJSON_ASSERT(_size < _capacity);
-    return JsonString(_pool->saveStringFromFreeZone(_size), _size,
+    ARDUINOJSON_ASSERT(size_ < capacity_);
+    return JsonString(pool_->saveStringFromFreeZone(size_), size_,
                       JsonString::Copied);
   }
 
   size_t write(uint8_t c) {
-    if (_size >= _capacity)
+    if (size_ >= capacity_)
       return 0;
 
-    _string[_size++] = char(c);
+    string_[size_++] = char(c);
     return 1;
   }
 
   size_t write(const uint8_t* buffer, size_t size) {
-    if (_size + size >= _capacity) {
-      _size = _capacity;  // mark as overflowed
+    if (size_ + size >= capacity_) {
+      size_ = capacity_;  // mark as overflowed
       return 0;
     }
-    memcpy(&_string[_size], buffer, size);
-    _size += size;
+    memcpy(&string_[size_], buffer, size);
+    size_ += size;
     return size;
   }
 
   bool overflowed() const {
-    return _size >= _capacity;
+    return size_ >= capacity_;
   }
 
  private:
-  MemoryPool* _pool;
-  size_t _size;
-  char* _string;
-  size_t _capacity;
+  MemoryPool* pool_;
+  size_t size_;
+  char* string_;
+  size_t capacity_;
 };
+}  // namespace detail
 
 inline void convertToJson(const ::Printable& src, JsonVariant dst) {
-  MemoryPool* pool = VariantAttorney::getPool(dst);
-  VariantData* data = VariantAttorney::getData(dst);
+  auto pool = detail::VariantAttorney::getPool(dst);
+  auto data = detail::VariantAttorney::getData(dst);
   if (!pool || !data)
     return;
-  MemoryPoolPrint print(pool);
+  detail::MemoryPoolPrint print(pool);
   src.printTo(print);
   if (print.overflowed()) {
     pool->markAsOverflowed();
@@ -305,15 +308,17 @@ inline bool canConvertFromJson(JsonVariantConst src, const std::string_view&) {
 
 #endif
 
+namespace detail {
 template <typename T>
 struct ConverterNeedsWriteableRef {
  protected:  // <- to avoid GCC's "all member functions in class are private"
-  static int probe(T (*f)(JsonVariant));
-  static char probe(T (*f)(JsonVariantConst));
+  static int probe(T (*f)(ArduinoJson::JsonVariant));
+  static char probe(T (*f)(ArduinoJson::JsonVariantConst));
 
  public:
   static const bool value =
       sizeof(probe(Converter<T>::fromJson)) == sizeof(int);
 };
+}  // namespace detail
 
-}  // namespace ARDUINOJSON_NAMESPACE
+ARDUINOJSON_END_PUBLIC_NAMESPACE
diff --git a/neosensor/libraries/ArduinoJson/src/ArduinoJson/Variant/JsonVariant.hpp b/neosensor/libraries/ArduinoJson/src/ArduinoJson/Variant/JsonVariant.hpp
index 95ac56e3..d1f122e9 100644
--- a/neosensor/libraries/ArduinoJson/src/ArduinoJson/Variant/JsonVariant.hpp
+++ b/neosensor/libraries/ArduinoJson/src/ArduinoJson/Variant/JsonVariant.hpp
@@ -1,58 +1,59 @@
 // ArduinoJson - https://arduinojson.org
-// Copyright © 2014-2022, Benoit BLANCHON
+// Copyright © 2014-2023, Benoit BLANCHON
 // MIT License
 
 #pragma once
 
 #include <ArduinoJson/Variant/VariantRefBase.hpp>
 
-namespace ARDUINOJSON_NAMESPACE {
+ARDUINOJSON_BEGIN_PUBLIC_NAMESPACE
 
 // A reference to a value in a JsonDocument.
 // https://arduinojson.org/v6/api/jsonvariant/
-class JsonVariant : public VariantRefBase<JsonVariant>,
-                    public VariantOperators<JsonVariant> {
-  friend class VariantAttorney;
+class JsonVariant : public detail::VariantRefBase<JsonVariant>,
+                    public detail::VariantOperators<JsonVariant> {
+  friend class detail::VariantAttorney;
 
  public:
   // Creates an unbound reference.
-  JsonVariant() : _data(0), _pool(0) {}
+  JsonVariant() : data_(0), pool_(0) {}
 
   // INTERNAL USE ONLY
-  JsonVariant(MemoryPool* pool, VariantData* data) : _data(data), _pool(pool) {}
+  JsonVariant(detail::MemoryPool* pool, detail::VariantData* data)
+      : data_(data), pool_(pool) {}
 
  private:
-  FORCE_INLINE MemoryPool* getPool() const {
-    return _pool;
+  FORCE_INLINE detail::MemoryPool* getPool() const {
+    return pool_;
   }
 
-  FORCE_INLINE VariantData* getData() const {
-    return _data;
+  FORCE_INLINE detail::VariantData* getData() const {
+    return data_;
   }
 
-  FORCE_INLINE VariantData* getOrCreateData() const {
-    return _data;
+  FORCE_INLINE detail::VariantData* getOrCreateData() const {
+    return data_;
   }
 
-  VariantData* _data;
-  MemoryPool* _pool;
+  detail::VariantData* data_;
+  detail::MemoryPool* pool_;
 };
 
 template <>
-struct Converter<JsonVariant> : private VariantAttorney {
+struct Converter<JsonVariant> : private detail::VariantAttorney {
   static void toJson(JsonVariant src, JsonVariant dst) {
-    variantCopyFrom(getData(dst), getData(src), getPool(dst));
+    detail::variantCopyFrom(getData(dst), getData(src), getPool(dst));
   }
 
   static JsonVariant fromJson(JsonVariant src) {
     return src;
   }
 
-  static InvalidConversion<JsonVariantConst, JsonVariant> fromJson(
+  static detail::InvalidConversion<JsonVariantConst, JsonVariant> fromJson(
       JsonVariantConst);
 
   static bool checkJson(JsonVariant src) {
-    VariantData* data = getData(src);
+    auto data = getData(src);
     return !!data;
   }
 
@@ -62,7 +63,7 @@ struct Converter<JsonVariant> : private VariantAttorney {
 };
 
 template <>
-struct Converter<JsonVariantConst> : private VariantAttorney {
+struct Converter<JsonVariantConst> : private detail::VariantAttorney {
   static void toJson(JsonVariantConst src, JsonVariant dst) {
     variantCopyFrom(getData(dst), getData(src), getPool(dst));
   }
@@ -72,9 +73,9 @@ struct Converter<JsonVariantConst> : private VariantAttorney {
   }
 
   static bool checkJson(JsonVariantConst src) {
-    const VariantData* data = getData(src);
+    auto data = getData(src);
     return !!data;
   }
 };
 
-}  // namespace ARDUINOJSON_NAMESPACE
+ARDUINOJSON_END_PUBLIC_NAMESPACE
diff --git a/neosensor/libraries/ArduinoJson/src/ArduinoJson/Variant/JsonVariantConst.hpp b/neosensor/libraries/ArduinoJson/src/ArduinoJson/Variant/JsonVariantConst.hpp
index c15202e0..606df083 100644
--- a/neosensor/libraries/ArduinoJson/src/ArduinoJson/Variant/JsonVariantConst.hpp
+++ b/neosensor/libraries/ArduinoJson/src/ArduinoJson/Variant/JsonVariantConst.hpp
@@ -1,5 +1,5 @@
 // ArduinoJson - https://arduinojson.org
-// Copyright © 2014-2022, Benoit BLANCHON
+// Copyright © 2014-2023, Benoit BLANCHON
 // MIT License
 
 #pragma once
@@ -16,7 +16,7 @@
 #include <ArduinoJson/Variant/VariantOperators.hpp>
 #include <ArduinoJson/Variant/VariantTag.hpp>
 
-namespace ARDUINOJSON_NAMESPACE {
+ARDUINOJSON_BEGIN_PUBLIC_NAMESPACE
 
 // Forward declarations.
 class JsonArray;
@@ -24,63 +24,64 @@ class JsonObject;
 
 // A read-only reference to a value in a JsonDocument
 // https://arduinojson.org/v6/api/jsonarrayconst/
-class JsonVariantConst : public VariantTag,
-                         public VariantOperators<JsonVariantConst> {
-  friend class VariantAttorney;
+class JsonVariantConst : public detail::VariantTag,
+                         public detail::VariantOperators<JsonVariantConst> {
+  friend class detail::VariantAttorney;
 
  public:
   // Creates an unbound reference.
-  JsonVariantConst() : _data(0) {}
+  JsonVariantConst() : data_(0) {}
 
   // INTERNAL USE ONLY
-  explicit JsonVariantConst(const VariantData* data) : _data(data) {}
+  explicit JsonVariantConst(const detail::VariantData* data) : data_(data) {}
 
   // Returns true if the value is null or the reference is unbound.
   // https://arduinojson.org/v6/api/jsonvariantconst/isnull/
   FORCE_INLINE bool isNull() const {
-    return variantIsNull(_data);
+    using namespace detail;
+    return variantIsNull(data_);
   }
 
   // Returns true if the reference is unbound.
   FORCE_INLINE bool isUnbound() const {
-    return !_data;
+    return !data_;
   }
 
   // Returns the number of bytes occupied by the value.
   // https://arduinojson.org/v6/api/jsonvariantconst/memoryusage/
   FORCE_INLINE size_t memoryUsage() const {
-    return _data ? _data->memoryUsage() : 0;
+    return data_ ? data_->memoryUsage() : 0;
   }
 
   // Returns the depth (nesting level) of the value.
   // https://arduinojson.org/v6/api/jsonvariantconst/nesting/
   FORCE_INLINE size_t nesting() const {
-    return variantNesting(_data);
+    return variantNesting(data_);
   }
 
   // Returns the size of the array or object.
   // https://arduinojson.org/v6/api/jsonvariantconst/size/
   size_t size() const {
-    return variantSize(_data);
+    return variantSize(data_);
   }
 
   // Casts the value to the specified type.
   // https://arduinojson.org/v6/api/jsonvariantconst/as/
   template <typename T>
-  FORCE_INLINE
-      typename enable_if<!is_same<T, char*>::value && !is_same<T, char>::value,
-                         T>::type
-      as() const {
+  FORCE_INLINE typename detail::enable_if<!detail::is_same<T, char*>::value &&
+                                              !detail::is_same<T, char>::value,
+                                          T>::type
+  as() const {
     return Converter<T>::fromJson(*this);
   }
 
   // Returns true if the value is of the specified type.
   // https://arduinojson.org/v6/api/jsonvariantconst/is/
   template <typename T>
-  FORCE_INLINE
-      typename enable_if<!is_same<T, char*>::value && !is_same<T, char>::value,
-                         bool>::type
-      is() const {
+  FORCE_INLINE typename detail::enable_if<!detail::is_same<T, char*>::value &&
+                                              !detail::is_same<T, char>::value,
+                                          bool>::type
+  is() const {
     return Converter<T>::checkJson(*this);
   }
 
@@ -92,50 +93,52 @@ class JsonVariantConst : public VariantTag,
   // Gets array's element at specified index.
   // https://arduinojson.org/v6/api/jsonvariantconst/subscript/
   FORCE_INLINE JsonVariantConst operator[](size_t index) const {
-    return JsonVariantConst(variantGetElement(_data, index));
+    return JsonVariantConst(variantGetElement(data_, index));
   }
 
   // Gets object's member with specified key.
   // https://arduinojson.org/v6/api/jsonvariantconst/subscript/
   template <typename TString>
-  FORCE_INLINE
-      typename enable_if<IsString<TString>::value, JsonVariantConst>::type
-      operator[](const TString& key) const {
-    return JsonVariantConst(variantGetMember(_data, adaptString(key)));
+  FORCE_INLINE typename detail::enable_if<detail::IsString<TString>::value,
+                                          JsonVariantConst>::type
+  operator[](const TString& key) const {
+    return JsonVariantConst(variantGetMember(data_, detail::adaptString(key)));
   }
 
   // Gets object's member with specified key.
   // https://arduinojson.org/v6/api/jsonvariantconst/subscript/
   template <typename TChar>
-  FORCE_INLINE
-      typename enable_if<IsString<TChar*>::value, JsonVariantConst>::type
-      operator[](TChar* key) const {
-    return JsonVariantConst(variantGetMember(_data, adaptString(key)));
+  FORCE_INLINE typename detail::enable_if<detail::IsString<TChar*>::value,
+                                          JsonVariantConst>::type
+  operator[](TChar* key) const {
+    return JsonVariantConst(variantGetMember(data_, detail::adaptString(key)));
   }
 
   // Returns true if tge object contains the specified key.
   // https://arduinojson.org/v6/api/jsonvariantconst/containskey/
   template <typename TString>
-  FORCE_INLINE typename enable_if<IsString<TString>::value, bool>::type
-  containsKey(const TString& key) const {
-    return variantGetMember(getData(), adaptString(key)) != 0;
+  FORCE_INLINE
+      typename detail::enable_if<detail::IsString<TString>::value, bool>::type
+      containsKey(const TString& key) const {
+    return variantGetMember(getData(), detail::adaptString(key)) != 0;
   }
 
   // Returns true if tge object contains the specified key.
   // https://arduinojson.org/v6/api/jsonvariantconst/containskey/
   template <typename TChar>
-  FORCE_INLINE typename enable_if<IsString<TChar*>::value, bool>::type
-  containsKey(TChar* key) const {
-    return variantGetMember(getData(), adaptString(key)) != 0;
+  FORCE_INLINE
+      typename detail::enable_if<detail::IsString<TChar*>::value, bool>::type
+      containsKey(TChar* key) const {
+    return variantGetMember(getData(), detail::adaptString(key)) != 0;
   }
 
  protected:
-  const VariantData* getData() const {
-    return _data;
+  const detail::VariantData* getData() const {
+    return data_;
   }
 
  private:
-  const VariantData* _data;
+  const detail::VariantData* data_;
 };
 
-}  // namespace ARDUINOJSON_NAMESPACE
+ARDUINOJSON_END_PUBLIC_NAMESPACE
diff --git a/neosensor/libraries/ArduinoJson/src/ArduinoJson/Variant/SlotFunctions.hpp b/neosensor/libraries/ArduinoJson/src/ArduinoJson/Variant/SlotFunctions.hpp
index 1b5b5228..8fee77f2 100644
--- a/neosensor/libraries/ArduinoJson/src/ArduinoJson/Variant/SlotFunctions.hpp
+++ b/neosensor/libraries/ArduinoJson/src/ArduinoJson/Variant/SlotFunctions.hpp
@@ -1,5 +1,5 @@
 // ArduinoJson - https://arduinojson.org
-// Copyright © 2014-2022, Benoit BLANCHON
+// Copyright © 2014-2023, Benoit BLANCHON
 // MIT License
 
 #pragma once
@@ -7,20 +7,20 @@
 #include <ArduinoJson/Polyfills/assert.hpp>
 #include <ArduinoJson/Variant/VariantData.hpp>
 
-namespace ARDUINOJSON_NAMESPACE {
+ARDUINOJSON_BEGIN_PRIVATE_NAMESPACE
 
 struct SlotKeySetter {
-  SlotKeySetter(VariantSlot* instance) : _instance(instance) {}
+  SlotKeySetter(VariantSlot* instance) : instance_(instance) {}
 
   template <typename TStoredString>
   void operator()(TStoredString s) {
     if (!s)
       return;
-    ARDUINOJSON_ASSERT(_instance != 0);
-    _instance->setKey(s);
+    ARDUINOJSON_ASSERT(instance_ != 0);
+    instance_->setKey(s);
   }
 
-  VariantSlot* _instance;
+  VariantSlot* instance_;
 };
 
 template <typename TAdaptedString>
@@ -42,4 +42,4 @@ inline size_t slotSize(const VariantSlot* var) {
 inline VariantData* slotData(VariantSlot* slot) {
   return reinterpret_cast<VariantData*>(slot);
 }
-}  // namespace ARDUINOJSON_NAMESPACE
+ARDUINOJSON_END_PRIVATE_NAMESPACE
diff --git a/neosensor/libraries/ArduinoJson/src/ArduinoJson/Variant/VariantAttorney.hpp b/neosensor/libraries/ArduinoJson/src/ArduinoJson/Variant/VariantAttorney.hpp
index ee4f1713..a2f6815f 100644
--- a/neosensor/libraries/ArduinoJson/src/ArduinoJson/Variant/VariantAttorney.hpp
+++ b/neosensor/libraries/ArduinoJson/src/ArduinoJson/Variant/VariantAttorney.hpp
@@ -1,5 +1,5 @@
 // ArduinoJson - https://arduinojson.org
-// Copyright © 2014-2022, Benoit BLANCHON
+// Copyright © 2014-2023, Benoit BLANCHON
 // MIT License
 
 #pragma once
@@ -9,7 +9,7 @@
 #include <ArduinoJson/Variant/VariantTo.hpp>
 #include "JsonVariantConst.hpp"
 
-namespace ARDUINOJSON_NAMESPACE {
+ARDUINOJSON_BEGIN_PRIVATE_NAMESPACE
 
 // Grants access to the internal variant API
 class VariantAttorney {
@@ -45,4 +45,4 @@ class VariantAttorney {
   }
 };
 
-}  // namespace ARDUINOJSON_NAMESPACE
+ARDUINOJSON_END_PRIVATE_NAMESPACE
diff --git a/neosensor/libraries/ArduinoJson/src/ArduinoJson/Variant/VariantCompare.hpp b/neosensor/libraries/ArduinoJson/src/ArduinoJson/Variant/VariantCompare.hpp
index b6e7ea8c..42445c6b 100644
--- a/neosensor/libraries/ArduinoJson/src/ArduinoJson/Variant/VariantCompare.hpp
+++ b/neosensor/libraries/ArduinoJson/src/ArduinoJson/Variant/VariantCompare.hpp
@@ -1,5 +1,5 @@
 // ArduinoJson - https://arduinojson.org
-// Copyright © 2014-2022, Benoit BLANCHON
+// Copyright © 2014-2023, Benoit BLANCHON
 // MIT License
 
 #pragma once
@@ -10,7 +10,7 @@
 #include <ArduinoJson/Strings/StringAdapters.hpp>
 #include <ArduinoJson/Variant/Visitor.hpp>
 
-namespace ARDUINOJSON_NAMESPACE {
+ARDUINOJSON_BEGIN_PRIVATE_NAMESPACE
 
 class CollectionData;
 
@@ -75,20 +75,18 @@ struct NullComparer : ComparerBase {
   }
 };
 
-#if ARDUINOJSON_HAS_NULLPTR
 template <>
 struct Comparer<decltype(nullptr), void> : NullComparer {
   explicit Comparer(decltype(nullptr)) : NullComparer() {}
 };
-#endif
 
 struct ArrayComparer : ComparerBase {
-  const CollectionData* _rhs;
+  const CollectionData* rhs_;
 
-  explicit ArrayComparer(const CollectionData& rhs) : _rhs(&rhs) {}
+  explicit ArrayComparer(const CollectionData& rhs) : rhs_(&rhs) {}
 
   CompareResult visitArray(const CollectionData& lhs) {
-    if (JsonArrayConst(&lhs) == JsonArrayConst(_rhs))
+    if (JsonArrayConst(&lhs) == JsonArrayConst(rhs_))
       return COMPARE_RESULT_EQUAL;
     else
       return COMPARE_RESULT_DIFFER;
@@ -96,12 +94,12 @@ struct ArrayComparer : ComparerBase {
 };
 
 struct ObjectComparer : ComparerBase {
-  const CollectionData* _rhs;
+  const CollectionData* rhs_;
 
-  explicit ObjectComparer(const CollectionData& rhs) : _rhs(&rhs) {}
+  explicit ObjectComparer(const CollectionData& rhs) : rhs_(&rhs) {}
 
   CompareResult visitObject(const CollectionData& lhs) {
-    if (JsonObjectConst(&lhs) == JsonObjectConst(_rhs))
+    if (JsonObjectConst(&lhs) == JsonObjectConst(rhs_))
       return COMPARE_RESULT_EQUAL;
     else
       return COMPARE_RESULT_DIFFER;
@@ -109,15 +107,15 @@ struct ObjectComparer : ComparerBase {
 };
 
 struct RawComparer : ComparerBase {
-  const char* _rhsData;
-  size_t _rhsSize;
+  const char* rhsData_;
+  size_t rhsSize_;
 
   explicit RawComparer(const char* rhsData, size_t rhsSize)
-      : _rhsData(rhsData), _rhsSize(rhsSize) {}
+      : rhsData_(rhsData), rhsSize_(rhsSize) {}
 
   CompareResult visitRawJson(const char* lhsData, size_t lhsSize) {
-    size_t size = _rhsSize < lhsSize ? _rhsSize : lhsSize;
-    int n = memcmp(lhsData, _rhsData, size);
+    size_t size = rhsSize_ < lhsSize ? rhsSize_ : lhsSize;
+    int n = memcmp(lhsData, rhsData_, size);
     if (n < 0)
       return COMPARE_RESULT_LESS;
     else if (n > 0)
@@ -193,17 +191,17 @@ struct VariantComparer : ComparerBase {
 };
 
 template <typename T>
-struct Comparer<
-    T, typename enable_if<is_convertible<T, JsonVariantConst>::value>::type>
+struct Comparer<T, typename enable_if<is_convertible<
+                       T, ArduinoJson::JsonVariantConst>::value>::type>
     : VariantComparer {
   explicit Comparer(const T& value)
       : VariantComparer(VariantAttorney::getData(value)) {}
 };
 
 template <typename T>
-CompareResult compare(JsonVariantConst lhs, const T& rhs) {
+CompareResult compare(ArduinoJson::JsonVariantConst lhs, const T& rhs) {
   Comparer<T> comparer(rhs);
   return variantAccept(VariantAttorney::getData(lhs), comparer);
 }
 
-}  // namespace ARDUINOJSON_NAMESPACE
+ARDUINOJSON_END_PRIVATE_NAMESPACE
diff --git a/neosensor/libraries/ArduinoJson/src/ArduinoJson/Variant/VariantContent.hpp b/neosensor/libraries/ArduinoJson/src/ArduinoJson/Variant/VariantContent.hpp
index e904b337..22239019 100644
--- a/neosensor/libraries/ArduinoJson/src/ArduinoJson/Variant/VariantContent.hpp
+++ b/neosensor/libraries/ArduinoJson/src/ArduinoJson/Variant/VariantContent.hpp
@@ -1,5 +1,5 @@
 // ArduinoJson - https://arduinojson.org
-// Copyright © 2014-2022, Benoit BLANCHON
+// Copyright © 2014-2023, Benoit BLANCHON
 // MIT License
 
 #pragma once
@@ -10,7 +10,7 @@
 #include <ArduinoJson/Numbers/JsonFloat.hpp>
 #include <ArduinoJson/Numbers/JsonInteger.hpp>
 
-namespace ARDUINOJSON_NAMESPACE {
+ARDUINOJSON_BEGIN_PRIVATE_NAMESPACE
 
 enum {
   VALUE_MASK = 0x7F,
@@ -54,4 +54,5 @@ union VariantContent {
     size_t size;
   } asString;
 };
-}  // namespace ARDUINOJSON_NAMESPACE
+
+ARDUINOJSON_END_PRIVATE_NAMESPACE
diff --git a/neosensor/libraries/ArduinoJson/src/ArduinoJson/Variant/VariantData.hpp b/neosensor/libraries/ArduinoJson/src/ArduinoJson/Variant/VariantData.hpp
index d086accc..7ef218c0 100644
--- a/neosensor/libraries/ArduinoJson/src/ArduinoJson/Variant/VariantData.hpp
+++ b/neosensor/libraries/ArduinoJson/src/ArduinoJson/Variant/VariantData.hpp
@@ -1,5 +1,5 @@
 // ArduinoJson - https://arduinojson.org
-// Copyright © 2014-2022, Benoit BLANCHON
+// Copyright © 2014-2023, Benoit BLANCHON
 // MIT License
 
 #pragma once
@@ -11,67 +11,50 @@
 #include <ArduinoJson/Strings/StringAdapters.hpp>
 #include <ArduinoJson/Variant/VariantContent.hpp>
 
-// VariantData can't have a constructor (to be a POD), so we have no way to fix
-// this warning
-#if defined(__GNUC__)
-#  if __GNUC__ >= 7
-#    pragma GCC diagnostic push
-#    pragma GCC diagnostic ignored "-Wmaybe-uninitialized"
-#    pragma GCC diagnostic ignored "-Wuninitialized"
-#  endif
-#endif
-
-namespace ARDUINOJSON_NAMESPACE {
+ARDUINOJSON_BEGIN_PRIVATE_NAMESPACE
 
 class VariantData {
-  VariantContent _content;  // must be first to allow cast from array to variant
-  uint8_t _flags;
+  VariantContent content_;  // must be first to allow cast from array to variant
+  uint8_t flags_;
 
  public:
-  // Must be a POD!
-  // - no constructor
-  // - no destructor
-  // - no virtual
-  // - no inheritance
-  void init() {
-    _flags = VALUE_IS_NULL;
-  }
+  VariantData() : flags_(VALUE_IS_NULL) {}
 
   void operator=(const VariantData& src) {
-    _content = src._content;
-    _flags = uint8_t((_flags & OWNED_KEY_BIT) | (src._flags & ~OWNED_KEY_BIT));
+    content_ = src.content_;
+    flags_ = uint8_t((flags_ & OWNED_KEY_BIT) | (src.flags_ & ~OWNED_KEY_BIT));
   }
 
   template <typename TVisitor>
   typename TVisitor::result_type accept(TVisitor& visitor) const {
     switch (type()) {
       case VALUE_IS_FLOAT:
-        return visitor.visitFloat(_content.asFloat);
+        return visitor.visitFloat(content_.asFloat);
 
       case VALUE_IS_ARRAY:
-        return visitor.visitArray(_content.asCollection);
+        return visitor.visitArray(content_.asCollection);
 
       case VALUE_IS_OBJECT:
-        return visitor.visitObject(_content.asCollection);
+        return visitor.visitObject(content_.asCollection);
 
       case VALUE_IS_LINKED_STRING:
       case VALUE_IS_OWNED_STRING:
-        return visitor.visitString(_content.asString.data,
-                                   _content.asString.size);
+        return visitor.visitString(content_.asString.data,
+                                   content_.asString.size);
 
       case VALUE_IS_OWNED_RAW:
       case VALUE_IS_LINKED_RAW:
-        return visitor.visitRawJson(_content.asString.data,
-                                    _content.asString.size);
+        return visitor.visitRawJson(content_.asString.data,
+                                    content_.asString.size);
 
       case VALUE_IS_SIGNED_INTEGER:
-        return visitor.visitSignedInteger(_content.asSignedInteger);
+        return visitor.visitSignedInteger(content_.asSignedInteger);
 
       case VALUE_IS_UNSIGNED_INTEGER:
-        return visitor.visitUnsignedInteger(_content.asUnsignedInteger);
+        return visitor.visitUnsignedInteger(content_.asUnsignedInteger);
 
       case VALUE_IS_BOOLEAN:
-        return visitor.visitBoolean(_content.asBoolean != 0);
+        return visitor.visitBoolean(content_.asBoolean != 0);
 
       default:
         return visitor.visitNull();
@@ -89,7 +72,7 @@ class VariantData {
   bool asBoolean() const;
 
   CollectionData* asArray() {
-    return isArray() ? &_content.asCollection : 0;
+    return isArray() ? &content_.asCollection : 0;
   }
 
   const CollectionData* asArray() const {
@@ -97,11 +80,11 @@ class VariantData {
   }
 
   const CollectionData* asCollection() const {
-    return isCollection() ? &_content.asCollection : 0;
+    return isCollection() ? &content_.asCollection : 0;
   }
 
   CollectionData* asObject() {
-    return isObject() ? &_content.asCollection : 0;
+    return isObject() ? &content_.asCollection : 0;
   }
 
   const CollectionData* asObject() const {
@@ -111,7 +94,7 @@ class VariantData {
   bool copyFrom(const VariantData& src, MemoryPool* pool);
 
   bool isArray() const {
-    return (_flags & VALUE_IS_ARRAY) != 0;
+    return (flags_ & VALUE_IS_ARRAY) != 0;
   }
 
   bool isBoolean() const {
@@ -119,17 +102,17 @@ class VariantData {
   }
 
   bool isCollection() const {
-    return (_flags & COLLECTION_MASK) != 0;
+    return (flags_ & COLLECTION_MASK) != 0;
   }
 
   template <typename T>
   bool isInteger() const {
     switch (type()) {
       case VALUE_IS_UNSIGNED_INTEGER:
-        return canConvertNumber<T>(_content.asUnsignedInteger);
+        return canConvertNumber<T>(content_.asUnsignedInteger);
 
       case VALUE_IS_SIGNED_INTEGER:
-        return canConvertNumber<T>(_content.asSignedInteger);
+        return canConvertNumber<T>(content_.asSignedInteger);
 
       default:
         return false;
@@ -137,7 +120,7 @@ class VariantData {
   }
 
   bool isFloat() const {
-    return (_flags & NUMBER_BIT) != 0;
+    return (flags_ & NUMBER_BIT) != 0;
   }
 
   bool isString() const {
@@ -145,7 +128,7 @@ class VariantData {
   }
 
   bool isObject() const {
-    return (_flags & VALUE_IS_OBJECT) != 0;
+    return (flags_ & VALUE_IS_OBJECT) != 0;
   }
 
   bool isNull() const {
@@ -158,30 +141,30 @@ class VariantData {
 
   void remove(size_t index) {
     if (isArray())
-      _content.asCollection.removeElement(index);
+      content_.asCollection.removeElement(index);
   }
 
   template <typename TAdaptedString>
   void remove(TAdaptedString key) {
     if (isObject())
-      _content.asCollection.removeMember(key);
+      content_.asCollection.removeMember(key);
   }
 
   void setBoolean(bool value) {
     setType(VALUE_IS_BOOLEAN);
-    _content.asBoolean = value;
+    content_.asBoolean = value;
   }
 
   void setFloat(JsonFloat value) {
     setType(VALUE_IS_FLOAT);
-    _content.asFloat = value;
+    content_.asFloat = value;
   }
 
   void setLinkedRaw(SerializedValue<const char*> value) {
     if (value.data()) {
       setType(VALUE_IS_LINKED_RAW);
-      _content.asString.data = value.data();
-      _content.asString.size = value.size();
+      content_.asString.data = value.data();
+      content_.asString.size = value.size();
     } else {
       setType(VALUE_IS_NULL);
     }
@@ -192,8 +175,8 @@ class VariantData {
     const char* dup = pool->saveString(adaptString(value.data(), value.size()));
     if (dup) {
       setType(VALUE_IS_OWNED_RAW);
-      _content.asString.data = dup;
-      _content.asString.size = value.size();
+      content_.asString.data = dup;
+      content_.asString.size = value.size();
       return true;
     } else {
       setType(VALUE_IS_NULL);
@@ -204,13 +187,13 @@ class VariantData {
   template <typename T>
   typename enable_if<is_unsigned<T>::value>::type setInteger(T value) {
     setType(VALUE_IS_UNSIGNED_INTEGER);
-    _content.asUnsignedInteger = static_cast<JsonUInt>(value);
+    content_.asUnsignedInteger = static_cast<JsonUInt>(value);
   }
 
   template <typename T>
   typename enable_if<is_signed<T>::value>::type setInteger(T value) {
     setType(VALUE_IS_SIGNED_INTEGER);
-    _content.asSignedInteger = value;
+    content_.asSignedInteger = value;
   }
 
   void setNull() {
@@ -223,20 +206,20 @@ class VariantData {
       setType(VALUE_IS_LINKED_STRING);
     else
       setType(VALUE_IS_OWNED_STRING);
-    _content.asString.data = s.c_str();
-    _content.asString.size = s.size();
+    content_.asString.data = s.c_str();
+    content_.asString.size = s.size();
   }
 
   CollectionData& toArray() {
     setType(VALUE_IS_ARRAY);
-    _content.asCollection.clear();
-    return _content.asCollection;
+    content_.asCollection.clear();
+    return content_.asCollection;
   }
 
   CollectionData& toObject() {
     setType(VALUE_IS_OBJECT);
-    _content.asCollection.clear();
-    return _content.asCollection;
+    content_.asCollection.clear();
+    return content_.asCollection;
   }
 
   size_t memoryUsage() const {
@@ -245,17 +228,17 @@ class VariantData {
       case VALUE_IS_OWNED_RAW:
         // We always add a zero at the end: the deduplication function uses it
         // to detect the beginning of the next string.
-        return _content.asString.size + 1;
+        return content_.asString.size + 1;
       case VALUE_IS_OBJECT:
       case VALUE_IS_ARRAY:
-        return _content.asCollection.memoryUsage();
+        return content_.asCollection.memoryUsage();
       default:
         return 0;
     }
   }
 
   size_t size() const {
-    return isCollection() ? _content.asCollection.size() : 0;
+    return isCollection() ? content_.asCollection.size() : 0;
   }
 
   VariantData* addElement(MemoryPool* pool) {
@@ -263,7 +246,7 @@ class VariantData {
       toArray();
     if (!isArray())
       return 0;
-    return _content.asCollection.addElement(pool);
+    return content_.asCollection.addElement(pool);
   }
 
   VariantData* getElement(size_t index) const {
@@ -276,7 +259,7 @@ class VariantData {
       toArray();
     if (!isArray())
       return 0;
-    return _content.asCollection.getOrAddElement(index, pool);
+    return content_.asCollection.getOrAddElement(index, pool);
   }
 
   template <typename TAdaptedString>
@@ -291,18 +274,18 @@ class VariantData {
       toObject();
     if (!isObject())
       return 0;
-    return _content.asCollection.getOrAddMember(key, pool);
+    return content_.asCollection.getOrAddMember(key, pool);
   }
 
   void movePointers(ptrdiff_t stringDistance, ptrdiff_t variantDistance) {
-    if (_flags & OWNED_VALUE_BIT)
-      _content.asString.data += stringDistance;
-    if (_flags & COLLECTION_MASK)
-      _content.asCollection.movePointers(stringDistance, variantDistance);
+    if (flags_ & OWNED_VALUE_BIT)
+      content_.asString.data += stringDistance;
+    if (flags_ & COLLECTION_MASK)
+      content_.asCollection.movePointers(stringDistance, variantDistance);
   }
 
   uint8_t type() const {
-    return _flags & VALUE_MASK;
+    return flags_ & VALUE_MASK;
   }
 
   template <typename TAdaptedString>
@@ -317,29 +300,23 @@ class VariantData {
 
  private:
   void setType(uint8_t t) {
-    _flags &= OWNED_KEY_BIT;
-    _flags |= t;
+    flags_ &= OWNED_KEY_BIT;
+    flags_ |= t;
   }
 
   struct VariantStringSetter {
-    VariantStringSetter(VariantData* instance) : _instance(instance) {}
+    VariantStringSetter(VariantData* instance) : instance_(instance) {}
 
     template <typename TStoredString>
     void operator()(TStoredString s) {
       if (s)
-        _instance->setString(s);
+        instance_->setString(s);
       else
-        _instance->setNull();
+        instance_->setNull();
     }
 
-    VariantData* _instance;
+    VariantData* instance_;
   };
 };
 
-}  // namespace ARDUINOJSON_NAMESPACE
-
-#if defined(__GNUC__)
-#  if __GNUC__ >= 8
-#    pragma GCC diagnostic pop
-#  endif
-#endif
+ARDUINOJSON_END_PRIVATE_NAMESPACE
diff --git a/neosensor/libraries/ArduinoJson/src/ArduinoJson/Variant/VariantFunctions.hpp b/neosensor/libraries/ArduinoJson/src/ArduinoJson/Variant/VariantFunctions.hpp
index 99cc7cb1..c0c91957 100644
--- a/neosensor/libraries/ArduinoJson/src/ArduinoJson/Variant/VariantFunctions.hpp
+++ b/neosensor/libraries/ArduinoJson/src/ArduinoJson/Variant/VariantFunctions.hpp
@@ -1,5 +1,5 @@
 // ArduinoJson - https://arduinojson.org
-// Copyright © 2014-2022, Benoit BLANCHON
+// Copyright © 2014-2023, Benoit BLANCHON
 // MIT License
 
 #pragma once
@@ -9,7 +9,7 @@
 #include <ArduinoJson/Variant/VariantData.hpp>
 #include <ArduinoJson/Variant/Visitor.hpp>
 
-namespace ARDUINOJSON_NAMESPACE {
+ARDUINOJSON_BEGIN_PRIVATE_NAMESPACE
 
 template <typename TVisitor>
 inline typename TVisitor::result_type variantAccept(const VariantData* var,
@@ -110,4 +110,4 @@ inline size_t variantNesting(const VariantData* var) {
   return maxChildNesting + 1;
 }
 
-}  // namespace ARDUINOJSON_NAMESPACE
+ARDUINOJSON_END_PRIVATE_NAMESPACE
diff --git a/neosensor/libraries/ArduinoJson/src/ArduinoJson/Variant/VariantImpl.hpp b/neosensor/libraries/ArduinoJson/src/ArduinoJson/Variant/VariantImpl.hpp
index ced1b8a5..66a473bb 100644
--- a/neosensor/libraries/ArduinoJson/src/ArduinoJson/Variant/VariantImpl.hpp
+++ b/neosensor/libraries/ArduinoJson/src/ArduinoJson/Variant/VariantImpl.hpp
@@ -1,5 +1,5 @@
 // ArduinoJson - https://arduinojson.org
-// Copyright © 2014-2022, Benoit BLANCHON
+// Copyright © 2014-2023, Benoit BLANCHON
 // MIT License
 
 #pragma once
@@ -13,22 +13,22 @@
 
 #include <string.h>  // for strcmp
 
-namespace ARDUINOJSON_NAMESPACE {
+ARDUINOJSON_BEGIN_PRIVATE_NAMESPACE
 
 template <typename T>
 inline T VariantData::asIntegral() const {
   switch (type()) {
     case VALUE_IS_BOOLEAN:
-      return _content.asBoolean;
+      return content_.asBoolean;
     case VALUE_IS_UNSIGNED_INTEGER:
-      return convertNumber<T>(_content.asUnsignedInteger);
+      return convertNumber<T>(content_.asUnsignedInteger);
     case VALUE_IS_SIGNED_INTEGER:
-      return convertNumber<T>(_content.asSignedInteger);
+      return convertNumber<T>(content_.asSignedInteger);
     case VALUE_IS_LINKED_STRING:
     case VALUE_IS_OWNED_STRING:
-      return parseNumber<T>(_content.asString.data);
+      return parseNumber<T>(content_.asString.data);
     case VALUE_IS_FLOAT:
-      return convertNumber<T>(_content.asFloat);
+      return convertNumber<T>(content_.asFloat);
     default:
       return 0;
   }
@@ -37,12 +37,12 @@ inline T VariantData::asIntegral() const {
 inline bool VariantData::asBoolean() const {
   switch (type()) {
     case VALUE_IS_BOOLEAN:
-      return _content.asBoolean;
+      return content_.asBoolean;
     case VALUE_IS_SIGNED_INTEGER:
     case VALUE_IS_UNSIGNED_INTEGER:
-      return _content.asUnsignedInteger != 0;
+      return content_.asUnsignedInteger != 0;
     case VALUE_IS_FLOAT:
-      return _content.asFloat != 0;
+      return content_.asFloat != 0;
     case VALUE_IS_NULL:
       return false;
     default:
@@ -55,16 +55,16 @@ template <typename T>
 inline T VariantData::asFloat() const {
   switch (type()) {
     case VALUE_IS_BOOLEAN:
-      return static_cast<T>(_content.asBoolean);
+      return static_cast<T>(content_.asBoolean);
     case VALUE_IS_UNSIGNED_INTEGER:
-      return static_cast<T>(_content.asUnsignedInteger);
+      return static_cast<T>(content_.asUnsignedInteger);
     case VALUE_IS_SIGNED_INTEGER:
-      return static_cast<T>(_content.asSignedInteger);
+      return static_cast<T>(content_.asSignedInteger);
     case VALUE_IS_LINKED_STRING:
     case VALUE_IS_OWNED_STRING:
-      return parseNumber<T>(_content.asString.data);
+      return parseNumber<T>(content_.asString.data);
     case VALUE_IS_FLOAT:
-      return static_cast<T>(_content.asFloat);
+      return static_cast<T>(content_.asFloat);
     default:
       return 0;
   }
@@ -73,10 +73,10 @@ inline T VariantData::asFloat() const {
 inline JsonString VariantData::asString() const {
   switch (type()) {
     case VALUE_IS_LINKED_STRING:
-      return JsonString(_content.asString.data, _content.asString.size,
+      return JsonString(content_.asString.data, content_.asString.size,
                         JsonString::Linked);
     case VALUE_IS_OWNED_STRING:
-      return JsonString(_content.asString.data, _content.asString.size,
+      return JsonString(content_.asString.data, content_.asString.size,
                         JsonString::Copied);
     default:
       return JsonString();
@@ -86,20 +86,20 @@ inline JsonString VariantData::asString() const {
 inline bool VariantData::copyFrom(const VariantData& src, MemoryPool* pool) {
   switch (src.type()) {
     case VALUE_IS_ARRAY:
-      return toArray().copyFrom(src._content.asCollection, pool);
+      return toArray().copyFrom(src.content_.asCollection, pool);
     case VALUE_IS_OBJECT:
-      return toObject().copyFrom(src._content.asCollection, pool);
+      return toObject().copyFrom(src.content_.asCollection, pool);
     case VALUE_IS_OWNED_STRING: {
       JsonString value = src.asString();
       return setString(adaptString(value), pool);
     }
     case VALUE_IS_OWNED_RAW:
       return storeOwnedRaw(
-          serialized(src._content.asString.data, src._content.asString.size),
+          serialized(src.content_.asString.data, src.content_.asString.size),
           pool);
     default:
       setType(src.type());
-      _content = src._content;
+      content_ = src.content_;
       return true;
   }
 }
@@ -148,4 +148,4 @@ inline void convertToJson(const VariantRefBase<TDerived>& src,
   dst.set(src.template as<JsonVariantConst>());
 }
 
-}  // namespace ARDUINOJSON_NAMESPACE
+ARDUINOJSON_END_PRIVATE_NAMESPACE
diff --git a/neosensor/libraries/ArduinoJson/src/ArduinoJson/Variant/VariantOperators.hpp b/neosensor/libraries/ArduinoJson/src/ArduinoJson/Variant/VariantOperators.hpp
index b73ff937..a352f0c4 100644
--- a/neosensor/libraries/ArduinoJson/src/ArduinoJson/Variant/VariantOperators.hpp
+++ b/neosensor/libraries/ArduinoJson/src/ArduinoJson/Variant/VariantOperators.hpp
@@ -1,5 +1,5 @@
 // ArduinoJson - https://arduinojson.org
-// Copyright © 2014-2022, Benoit BLANCHON
+// Copyright © 2014-2023, Benoit BLANCHON
 // MIT License
 
 #pragma once
@@ -9,10 +9,14 @@
 #include <ArduinoJson/Polyfills/type_traits.hpp>
 #include <ArduinoJson/Variant/VariantTag.hpp>
 
-namespace ARDUINOJSON_NAMESPACE {
+ARDUINOJSON_BEGIN_PUBLIC_NAMESPACE
 
 class JsonVariantConst;
 
+ARDUINOJSON_END_PUBLIC_NAMESPACE
+
+ARDUINOJSON_BEGIN_PRIVATE_NAMESPACE
+
 template <typename T>
 CompareResult compare(JsonVariantConst lhs,
                       const T& rhs);  // VariantCompare.cpp
@@ -187,4 +191,5 @@ struct VariantOperators : VariantOperatorTag {
     return (compare(lhs, rhs) & COMPARE_RESULT_GREATER_OR_EQUAL) != 0;
   }
 };
-}  // namespace ARDUINOJSON_NAMESPACE
+
+ARDUINOJSON_END_PRIVATE_NAMESPACE
diff --git a/neosensor/libraries/ArduinoJson/src/ArduinoJson/Variant/VariantRefBase.hpp b/neosensor/libraries/ArduinoJson/src/ArduinoJson/Variant/VariantRefBase.hpp
index ddfcf0d6..2afdda6a 100644
--- a/neosensor/libraries/ArduinoJson/src/ArduinoJson/Variant/VariantRefBase.hpp
+++ b/neosensor/libraries/ArduinoJson/src/ArduinoJson/Variant/VariantRefBase.hpp
@@ -1,5 +1,5 @@
 // ArduinoJson - https://arduinojson.org
-// Copyright © 2014-2022, Benoit BLANCHON
+// Copyright © 2014-2023, Benoit BLANCHON
 // MIT License
 
 #pragma once
@@ -10,10 +10,11 @@
 #include <ArduinoJson/Variant/VariantOperators.hpp>
 #include <ArduinoJson/Variant/VariantTo.hpp>
 
-namespace ARDUINOJSON_NAMESPACE {
-
+ARDUINOJSON_BEGIN_PUBLIC_NAMESPACE
 class JsonVariant;
+ARDUINOJSON_END_PUBLIC_NAMESPACE
 
+ARDUINOJSON_BEGIN_PRIVATE_NAMESPACE
 template <typename>
 class ElementProxy;
 
@@ -46,11 +47,9 @@ class VariantRefBase : public VariantTag {
   // Casts the value to the specified type.
   // https://arduinojson.org/v6/api/jsonvariant/as/
   template <typename T>
-  FORCE_INLINE typename enable_if<!is_same<T, char*>::value &&
-                                      !is_same<T, char>::value &&
-                                      !ConverterNeedsWriteableRef<T>::value,
-                                  T>::type
-  as() const {
+  FORCE_INLINE
+      typename enable_if<!ConverterNeedsWriteableRef<T>::value, T>::type
+      as() const {
     return Converter<T>::fromJson(getVariantConst());
   }
 
@@ -109,7 +108,7 @@ class VariantRefBase : public VariantTag {
 
   // Shallow copies the specified value.
   // https://arduinojson.org/v6/api/jsonvariant/shallowcopy/
-  FORCE_INLINE void shallowCopy(JsonVariantConst target) {
+  FORCE_INLINE void shallowCopy(ArduinoJson::JsonVariantConst target) {
     VariantData* data = getOrCreateData();
     if (!data)
       return;
@@ -235,14 +234,14 @@ class VariantRefBase : public VariantTag {
   // https://arduinojson.org/v6/api/jsonvariant/subscript/
   template <typename TString>
   FORCE_INLINE typename enable_if<IsString<TString>::value,
-                                  MemberProxy<TDerived, TString> >::type
+                                  MemberProxy<TDerived, TString>>::type
   operator[](const TString& key) const;
 
   // Gets or sets an object member.
   // https://arduinojson.org/v6/api/jsonvariant/subscript/
   template <typename TChar>
   FORCE_INLINE typename enable_if<IsString<TChar*>::value,
-                                  MemberProxy<TDerived, TChar*> >::type
+                                  MemberProxy<TDerived, TChar*>>::type
   operator[](TChar* key) const;
 
   // Creates an array and adds it to the object.
@@ -287,13 +286,13 @@ class VariantRefBase : public VariantTag {
   }
 
  private:
-  FORCE_INLINE JsonVariant getVariant() const;
+  FORCE_INLINE ArduinoJson::JsonVariant getVariant() const;
 
-  FORCE_INLINE JsonVariantConst getVariantConst() const {
-    return JsonVariantConst(getData());
+  FORCE_INLINE ArduinoJson::JsonVariantConst getVariantConst() const {
+    return ArduinoJson::JsonVariantConst(getData());
   }
 
-  FORCE_INLINE JsonVariant getOrCreateVariant() const;
+  FORCE_INLINE ArduinoJson::JsonVariant getOrCreateVariant() const;
 };
 
-}  // namespace ARDUINOJSON_NAMESPACE
+ARDUINOJSON_END_PRIVATE_NAMESPACE
diff --git a/neosensor/libraries/ArduinoJson/src/ArduinoJson/Variant/VariantSlot.hpp b/neosensor/libraries/ArduinoJson/src/ArduinoJson/Variant/VariantSlot.hpp
index 5cd53bd3..f88e35ba 100644
--- a/neosensor/libraries/ArduinoJson/src/ArduinoJson/Variant/VariantSlot.hpp
+++ b/neosensor/libraries/ArduinoJson/src/ArduinoJson/Variant/VariantSlot.hpp
@@ -1,5 +1,5 @@
 // ArduinoJson - https://arduinojson.org
-// Copyright © 2014-2022, Benoit BLANCHON
+// Copyright © 2014-2023, Benoit BLANCHON
 // MIT License
 
 #pragma once
@@ -9,7 +9,7 @@
 #include <ArduinoJson/Polyfills/type_traits.hpp>
 #include <ArduinoJson/Variant/VariantContent.hpp>
 
-namespace ARDUINOJSON_NAMESPACE {
+ARDUINOJSON_BEGIN_PRIVATE_NAMESPACE
 
 typedef int_t<ARDUINOJSON_SLOT_OFFSET_SIZE * 8>::type VariantSlotDiff;
 
@@ -17,10 +17,10 @@ class VariantSlot {
   // CAUTION: same layout as VariantData
   // we cannot use composition because it adds padding
   // (+20% on ESP8266 for example)
-  VariantContent _content;
-  uint8_t _flags;
-  VariantSlotDiff _next;
-  const char* _key;
+  VariantContent content_;
+  uint8_t flags_;
+  VariantSlotDiff next_;
+  const char* key_;
 
  public:
   // Must be a POD!
@@ -30,15 +30,15 @@ class VariantSlot {
   // - no inheritance
 
   VariantData* data() {
-    return reinterpret_cast<VariantData*>(&_content);
+    return reinterpret_cast<VariantData*>(&content_);
   }
 
   const VariantData* data() const {
-    return reinterpret_cast<const VariantData*>(&_content);
+    return reinterpret_cast<const VariantData*>(&content_);
   }
 
   VariantSlot* next() {
-    return _next ? this + _next : 0;
+    return next_ ? this + next_ : 0;
   }
 
   const VariantSlot* next() const {
@@ -48,9 +48,9 @@ class VariantSlot {
   VariantSlot* next(size_t distance) {
     VariantSlot* slot = this;
     while (distance--) {
-      if (!slot->_next)
+      if (!slot->next_)
         return 0;
-      slot += slot->_next;
+      slot += slot->next_;
     }
     return slot;
   }
@@ -64,7 +64,7 @@ class VariantSlot {
                                     numeric_limits<VariantSlotDiff>::lowest());
     ARDUINOJSON_ASSERT(!slot || slot - this <=
                                     numeric_limits<VariantSlotDiff>::highest());
-    _next = VariantSlotDiff(slot ? slot - this : 0);
+    next_ = VariantSlotDiff(slot ? slot - this : 0);
   }
 
   void setNextNotNull(VariantSlot* slot) {
@@ -73,40 +73,40 @@ class VariantSlot {
                        numeric_limits<VariantSlotDiff>::lowest());
     ARDUINOJSON_ASSERT(slot - this <=
                        numeric_limits<VariantSlotDiff>::highest());
-    _next = VariantSlotDiff(slot - this);
+    next_ = VariantSlotDiff(slot - this);
   }
 
   void setKey(JsonString k) {
     ARDUINOJSON_ASSERT(k);
     if (k.isLinked())
-      _flags &= VALUE_MASK;
+      flags_ &= VALUE_MASK;
     else
-      _flags |= OWNED_KEY_BIT;
-    _key = k.c_str();
+      flags_ |= OWNED_KEY_BIT;
+    key_ = k.c_str();
   }
 
   const char* key() const {
-    return _key;
+    return key_;
   }
 
   bool ownsKey() const {
-    return (_flags & OWNED_KEY_BIT) != 0;
+    return (flags_ & OWNED_KEY_BIT) != 0;
   }
 
   void clear() {
-    _next = 0;
-    _flags = 0;
-    _key = 0;
+    next_ = 0;
+    flags_ = 0;
+    key_ = 0;
   }
 
   void movePointers(ptrdiff_t stringDistance, ptrdiff_t variantDistance) {
-    if (_flags & OWNED_KEY_BIT)
-      _key += stringDistance;
-    if (_flags & OWNED_VALUE_BIT)
-      _content.asString.data += stringDistance;
-    if (_flags & COLLECTION_MASK)
-      _content.asCollection.movePointers(stringDistance, variantDistance);
+    if (flags_ & OWNED_KEY_BIT)
+      key_ += stringDistance;
+    if (flags_ & OWNED_VALUE_BIT)
+      content_.asString.data += stringDistance;
+    if (flags_ & COLLECTION_MASK)
+      content_.asCollection.movePointers(stringDistance, variantDistance);
   }
 };
 
-}  // namespace ARDUINOJSON_NAMESPACE
+ARDUINOJSON_END_PRIVATE_NAMESPACE
diff --git a/neosensor/libraries/ArduinoJson/src/ArduinoJson/Variant/VariantTag.hpp b/neosensor/libraries/ArduinoJson/src/ArduinoJson/Variant/VariantTag.hpp
index 94ec38b9..0c5ee40d 100644
--- a/neosensor/libraries/ArduinoJson/src/ArduinoJson/Variant/VariantTag.hpp
+++ b/neosensor/libraries/ArduinoJson/src/ArduinoJson/Variant/VariantTag.hpp
@@ -1,16 +1,16 @@
 // ArduinoJson - https://arduinojson.org
-// Copyright © 2014-2022, Benoit BLANCHON
+// Copyright © 2014-2023, Benoit BLANCHON
 // MIT License
 
 #pragma once
 
 #include <ArduinoJson/Namespace.hpp>
 
-namespace ARDUINOJSON_NAMESPACE {
+ARDUINOJSON_BEGIN_PRIVATE_NAMESPACE
 
 struct VariantTag {};
 
 template <typename T>
 struct IsVariant : is_base_of<VariantTag, T> {};
 
-}  // namespace ARDUINOJSON_NAMESPACE
+ARDUINOJSON_END_PRIVATE_NAMESPACE
diff --git a/neosensor/libraries/ArduinoJson/src/ArduinoJson/Variant/VariantTo.hpp b/neosensor/libraries/ArduinoJson/src/ArduinoJson/Variant/VariantTo.hpp
index 1521d777..0ca56df6 100644
--- a/neosensor/libraries/ArduinoJson/src/ArduinoJson/Variant/VariantTo.hpp
+++ b/neosensor/libraries/ArduinoJson/src/ArduinoJson/Variant/VariantTo.hpp
@@ -1,16 +1,18 @@
 // ArduinoJson - https://arduinojson.org
-// Copyright © 2014-2022, Benoit BLANCHON
+// Copyright © 2014-2023, Benoit BLANCHON
 // MIT License
 
 #pragma once
 
 #include <ArduinoJson/Namespace.hpp>
 
-namespace ARDUINOJSON_NAMESPACE {
+ARDUINOJSON_BEGIN_PUBLIC_NAMESPACE
 class JsonArray;
 class JsonObject;
 class JsonVariant;
+ARDUINOJSON_END_PUBLIC_NAMESPACE
 
+ARDUINOJSON_BEGIN_PRIVATE_NAMESPACE
 // A metafunction that returns the type of the value returned by
 // JsonVariant::to<T>()
 template <typename T>
@@ -29,4 +31,4 @@ struct VariantTo<JsonVariant> {
   typedef JsonVariant type;
 };
 
-}  // namespace ARDUINOJSON_NAMESPACE
+ARDUINOJSON_END_PRIVATE_NAMESPACE
diff --git a/neosensor/libraries/ArduinoJson/src/ArduinoJson/Variant/Visitor.hpp b/neosensor/libraries/ArduinoJson/src/ArduinoJson/Variant/Visitor.hpp
index 6c79316a..f1c78416 100644
--- a/neosensor/libraries/ArduinoJson/src/ArduinoJson/Variant/Visitor.hpp
+++ b/neosensor/libraries/ArduinoJson/src/ArduinoJson/Variant/Visitor.hpp
@@ -1,5 +1,5 @@
 // ArduinoJson - https://arduinojson.org
-// Copyright © 2014-2022, Benoit BLANCHON
+// Copyright © 2014-2023, Benoit BLANCHON
 // MIT License
 
 #pragma once
@@ -8,7 +8,7 @@
 #include <ArduinoJson/Numbers/JsonFloat.hpp>
 #include <ArduinoJson/Numbers/JsonInteger.hpp>
 
-namespace ARDUINOJSON_NAMESPACE {
+ARDUINOJSON_BEGIN_PRIVATE_NAMESPACE
 
 template <typename TResult>
 struct Visitor {
@@ -51,4 +51,4 @@ struct Visitor {
   }
 };
 
-}  // namespace ARDUINOJSON_NAMESPACE
+ARDUINOJSON_END_PRIVATE_NAMESPACE
diff --git a/neosensor/libraries/ArduinoJson/src/ArduinoJson/compatibility.hpp b/neosensor/libraries/ArduinoJson/src/ArduinoJson/compatibility.hpp
index ca178a27..85769487 100644
--- a/neosensor/libraries/ArduinoJson/src/ArduinoJson/compatibility.hpp
+++ b/neosensor/libraries/ArduinoJson/src/ArduinoJson/compatibility.hpp
@@ -1,5 +1,5 @@
 // ArduinoJson - https://arduinojson.org
-// Copyright © 2014-2022, Benoit BLANCHON
+// Copyright © 2014-2023, Benoit BLANCHON
 // MIT License
 //
 // clang-format off
@@ -20,4 +20,6 @@
 #define JsonBuffer ARDUINOJSON_DEPRECATION_ERROR(JsonBuffer, class)
 #define RawJson ARDUINOJSON_DEPRECATION_ERROR(RawJson, function)
 
+#define ARDUINOJSON_NAMESPACE _Pragma ("GCC warning \"ARDUINOJSON_NAMESPACE is deprecated, use ArduinoJson instead\"") ArduinoJson
+
 #endif
diff --git a/neosensor/libraries/ArduinoJson/src/ArduinoJson/version.hpp b/neosensor/libraries/ArduinoJson/src/ArduinoJson/version.hpp
index 7f4aff88..f0ee0afa 100644
--- a/neosensor/libraries/ArduinoJson/src/ArduinoJson/version.hpp
+++ b/neosensor/libraries/ArduinoJson/src/ArduinoJson/version.hpp
@@ -1,10 +1,10 @@
 // ArduinoJson - https://arduinojson.org
-// Copyright © 2014-2022, Benoit BLANCHON
+// Copyright © 2014-2023, Benoit BLANCHON
 // MIT License
 
 #pragma once
 
-#define ARDUINOJSON_VERSION "6.20.0"
+#define ARDUINOJSON_VERSION "6.21.2"
 #define ARDUINOJSON_VERSION_MAJOR 6
-#define ARDUINOJSON_VERSION_MINOR 20
-#define ARDUINOJSON_VERSION_REVISION 0
+#define ARDUINOJSON_VERSION_MINOR 21
+#define ARDUINOJSON_VERSION_REVISION 2
diff --git a/neosensor/libraries/ArduinoJson/src/CMakeLists.txt b/neosensor/libraries/ArduinoJson/src/CMakeLists.txt
index ebc83a95..10aef808 100644
--- a/neosensor/libraries/ArduinoJson/src/CMakeLists.txt
+++ b/neosensor/libraries/ArduinoJson/src/CMakeLists.txt
@@ -1,5 +1,5 @@
 # ArduinoJson - https://arduinojson.org
-# Copyright © 2014-2022, Benoit BLANCHON
+# Copyright © 2014-2023, Benoit BLANCHON
 # MIT License
 
 # I have no idea what this is about, I simply followed the instructions from:
@@ -12,79 +12,80 @@ include(GNUInstallDirs)
 # Adding the install interface generator expression makes sure that the include
 # files are installed to the proper location (provided by GNUInstallDirs)
 target_include_directories(ArduinoJson
-    INTERFACE
-        $<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}>
-        $<INSTALL_INTERFACE:${CMAKE_INSTALL_INCLUDEDIR}>
+	INTERFACE
+		$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}>
+		$<INSTALL_INTERFACE:${CMAKE_INSTALL_INCLUDEDIR}>
 )
 
 target_compile_definitions(ArduinoJson
-    INTERFACE
-        ARDUINOJSON_DEBUG=$<CONFIG:Debug>
+	INTERFACE
+		ARDUINOJSON_DEBUG=$<CONFIG:Debug>
 )
 
 # locations are provided by GNUInstallDirs
 install(
-    TARGETS
-        ArduinoJson
-    EXPORT
-        ArduinoJson_Targets
-    ARCHIVE DESTINATION
-        ${CMAKE_INSTALL_LIBDIR}
-    LIBRARY DESTINATION
-        ${CMAKE_INSTALL_LIBDIR}
-    RUNTIME DESTINATION
-        ${CMAKE_INSTALL_BINDIR}
+	TARGETS
+		ArduinoJson
+	EXPORT
+		ArduinoJson_Targets
+	ARCHIVE DESTINATION
+		${CMAKE_INSTALL_LIBDIR}
+	LIBRARY DESTINATION
+		${CMAKE_INSTALL_LIBDIR}
+	RUNTIME DESTINATION
+		${CMAKE_INSTALL_BINDIR}
 )
 
 include(CMakePackageConfigHelpers)
 
-if(${CMAKE_VERSION} VERSION_GREATER "3.14.0") 
-    set(ARCH_INDEPENDENT "ARCH_INDEPENDENT")
+if(${CMAKE_VERSION} VERSION_GREATER "3.14.0")
+	set(ARCH_INDEPENDENT "ARCH_INDEPENDENT")
 endif()
 
 write_basic_package_version_file(
-        "${PROJECT_BINARY_DIR}/ArduinoJsonConfigVersion.cmake"
-    VERSION
-        ${PROJECT_VERSION}
-    COMPATIBILITY
-        SameMajorVersion
-    ${ARCH_INDEPENDENT}
+		"${PROJECT_BINARY_DIR}/ArduinoJsonConfigVersion.cmake"
+	VERSION
+		${PROJECT_VERSION}
+	COMPATIBILITY
+		SameMajorVersion
+	${ARCH_INDEPENDENT}
 )
 
 configure_package_config_file(
-        "${PROJECT_SOURCE_DIR}/extras/ArduinoJsonConfig.cmake.in"
-        "${PROJECT_BINARY_DIR}/ArduinoJsonConfig.cmake"
-    INSTALL_DESTINATION
-        ${CMAKE_INSTALL_DATAROOTDIR}/ArduinoJson/cmake)
+		"${PROJECT_SOURCE_DIR}/extras/ArduinoJsonConfig.cmake.in"
+		"${PROJECT_BINARY_DIR}/ArduinoJsonConfig.cmake"
+	INSTALL_DESTINATION
+		${CMAKE_INSTALL_DATAROOTDIR}/ArduinoJson/cmake
+)
 
 install(
-    EXPORT
-        ArduinoJson_Targets
-    FILE
-        ArduinoJsonTargets.cmake
-    DESTINATION
-        ${CMAKE_INSTALL_DATAROOTDIR}/ArduinoJson/cmake
+	EXPORT
+		ArduinoJson_Targets
+	FILE
+		ArduinoJsonTargets.cmake
+	DESTINATION
+		${CMAKE_INSTALL_DATAROOTDIR}/ArduinoJson/cmake
 )
 
 install(
-    FILES
-        "${PROJECT_BINARY_DIR}/ArduinoJsonConfig.cmake"
-        "${PROJECT_BINARY_DIR}/ArduinoJsonConfigVersion.cmake"
-    DESTINATION
-        "${CMAKE_INSTALL_DATAROOTDIR}/ArduinoJson/cmake"
+	FILES
+		"${PROJECT_BINARY_DIR}/ArduinoJsonConfig.cmake"
+		"${PROJECT_BINARY_DIR}/ArduinoJsonConfigVersion.cmake"
+	DESTINATION
+		"${CMAKE_INSTALL_DATAROOTDIR}/ArduinoJson/cmake"
 )
 
 install(
-    FILES
-        ArduinoJson.h
-        ArduinoJson.hpp
-    DESTINATION
-        include
+	FILES
+		ArduinoJson.h
+		ArduinoJson.hpp
+	DESTINATION
+		include
 )
 
 install(
-    DIRECTORY
-        "${CMAKE_CURRENT_SOURCE_DIR}/ArduinoJson"
-    DESTINATION
-        include
+	DIRECTORY
+		"${CMAKE_CURRENT_SOURCE_DIR}/ArduinoJson"
+	DESTINATION
+		include
 )
diff --git a/neosensor/neosensor.ino b/neosensor/neosensor.ino
index 27eba1ac..1a2b64c9 100644
--- a/neosensor/neosensor.ino
+++ b/neosensor/neosensor.ino
@@ -558,16 +558,12 @@ bool setupNTP( void ) {
 #ifdef ESP8266
   // register ntp sync callback
   settimeofday_cb( syncNTP_cb );
-  // [may.20] as default, NTP server is provided by the DHCP server
-  configTime( MYTZ, NTP_DEFAULT_SERVER1, NTP_DEFAULT_SERVER2, NTP_DEFAULT_SERVER3 );
 #elif ESP32
   // register ntp sync callback
   sntp_set_time_sync_notification_cb( _syncNTP_cb );
-  // [nov.21] now using timezone definition :)
-  configTzTime( MYTZ, NTP_DEFAULT_SERVER1, NTP_DEFAULT_SERVER2, NTP_DEFAULT_SERVER3 );
-  // [may.20] as default, NTP server is provided by the DHCP server
-  // configTime(gmtOffset_sec, daylightOffset_sec, NTP_DEFAULT_SERVER1, NTP_DEFAULT_SERVER2, NTP_DEFAULT_SERVER3 );
 #endif    
+  // [may.23] esp32/8266 unified API
+  configTzTime( MYTZ, NTP_DEFAULT_SERVER1, NTP_DEFAULT_SERVER2, NTP_DEFAULT_SERVER3 );
 
   log_flush();
   // the end ...
@@ -632,18 +628,22 @@ void processWIFIparameters( wifiParametersMgt *wp=nullptr ) {
 
 // ---
 // show WiFi protocols in use
-void display_wifi_protocol( wifi_interface_t ifx=WIFI_IF_STA ) {
+void display_wifi_protocol( void ) {
 
   uint8_t cur_protocols;
-  esp_err_t _err;
-  _err = esp_wifi_get_protocol(ifx, &cur_protocols);
+  int _err;
+#ifdef ESP32
+  _err = esp_wifi_get_protocol(WIFI_IF_STA, &cur_protocols);
   if( _err != ESP_OK ) {
     log_error(F("\n[WiFiProtocol] Could not get protocol!"));
     return;
   }
-  
-  log_debug(F("\n# WIFI protocols = 0x"));log_debug(cur_protocols,HEX);log_flush();
+#elif defined (ESP8266)
+  cur_protocols = (uint8_t)WiFi.getPhyMode();
+#endif
 
+  log_debug(F("\n# WIFI protocols = 0x"));log_debug(cur_protocols,HEX);log_flush();
+#ifdef ESP32
   if( cur_protocols & WIFI_PROTOCOL_11N ) {
     log_debug(F("\n\tWiFi_Protocol_11n"));
   }
@@ -653,6 +653,17 @@ void display_wifi_protocol( wifi_interface_t ifx=WIFI_IF_STA ) {
   if( cur_protocols & WIFI_PROTOCOL_11B ) {
     log_debug(F("\n\tWiFi_Protocol_11b"));
   }
+#elif defined (ESP8266)
+  if( cur_protocols==WIFI_PHY_MODE_11N ) {
+    log_debug(F("\n\tWiFi_Protocol_11n"));
+  }
+  else if( cur_protocols==WIFI_PHY_MODE_11G ) {
+    log_debug(F("\n\tWiFi_Protocol_11g"));
+  }
+  else if( cur_protocols==WIFI_PHY_MODE_11B ) {
+    log_debug(F("\n\tWiFi_Protocol_11b"));
+  }
+#endif
 }
 
 
-- 
GitLab