[protozero] 01/02: Imported Upstream version 1.0.0

Sebastiaan Couwenberg sebastic at moszumanska.debian.org
Sat Aug 22 09:47:07 UTC 2015


This is an automated email from the git hooks/post-receive script.

sebastic pushed a commit to branch master
in repository protozero.

commit 0c693538495144806c921b2499c4802f4d3fa035
Author: Bas Couwenberg <sebastic at xs4all.nl>
Date:   Sat Aug 22 10:08:20 2015 +0200

    Imported Upstream version 1.0.0
---
 .gitignore                                         |   14 +
 .travis.yml                                        |   58 +
 LICENSE.from_folly                                 |  177 +
 LICENSE.md                                         |   24 +
 Makefile                                           |  109 +
 README.md                                          |  115 +
 appveyor.yml                                       |   25 +
 common.gypi                                        |  143 +
 doc/.gitignore                                     |    2 +
 doc/Doxyfile                                       | 2352 +++++
 include/protozero/exception.hpp                    |   68 +
 include/protozero/pbf_reader.hpp                   | 1003 +++
 include/protozero/pbf_types.hpp                    |   49 +
 include/protozero/pbf_writer.hpp                   |  657 ++
 include/protozero/varint.hpp                       |  136 +
 protozero.gyp                                      |   52 +
 test/.gitignore                                    |    6 +
 test/README.md                                     |   46 +
 test/create_pbf_test_data.sh                       |   28 +
 test/include/catch.hpp                             | 9416 ++++++++++++++++++++
 test/include/test.hpp                              |   17 +
 test/include/testcase.hpp                          |   21 +
 test/t/.gitignore                                  |    6 +
 test/t/basic/test_cases.cpp                        |   47 +
 test/t/bool/data-also-true.pbf                     |    1 +
 test/t/bool/data-false.pbf                         |  Bin 0 -> 2 bytes
 test/t/bool/data-still-true.pbf                    |    1 +
 test/t/bool/data-true.pbf                          |    1 +
 test/t/bool/test_cases.cpp                         |   64 +
 test/t/bool/testcase.cpp                           |   20 +
 test/t/bool/testcase.proto                         |   11 +
 test/t/bool/writer_test_cases.cpp                  |   32 +
 test/t/bytes/data-binary.pbf                       |    2 +
 test/t/bytes/data-empty.pbf                        |  Bin 0 -> 2 bytes
 test/t/bytes/data-one.pbf                          |    2 +
 test/t/bytes/data-string.pbf                       |    2 +
 test/t/bytes/test_cases.cpp                        |   94 +
 test/t/bytes/testcase.cpp                          |   24 +
 test/t/bytes/testcase.proto                        |   11 +
 test/t/bytes/writer_test_cases.cpp                 |   54 +
 test/t/complex/data-all.pbf                        |  Bin 0 -> 62 bytes
 test/t/complex/data-minimal.pbf                    |  Bin 0 -> 15 bytes
 test/t/complex/data-some.pbf                       |  Bin 0 -> 30 bytes
 test/t/complex/test_cases.cpp                      |  345 +
 test/t/complex/testcase.cpp                        |   30 +
 test/t/complex/testcase.proto                      |   19 +
 test/t/double/data-neg.pbf                         |    1 +
 test/t/double/data-pos.pbf                         |    1 +
 test/t/double/data-zero.pbf                        |  Bin 0 -> 9 bytes
 test/t/double/test_cases.cpp                       |   69 +
 test/t/double/testcase.cpp                         |   17 +
 test/t/double/testcase.proto                       |   11 +
 test/t/double/writer_test_cases.cpp                |   38 +
 test/t/enum/data-black.pbf                         |  Bin 0 -> 2 bytes
 test/t/enum/data-blue.pbf                          |    1 +
 test/t/enum/test_cases.cpp                         |   44 +
 test/t/enum/testcase.cpp                           |   14 +
 test/t/enum/testcase.proto                         |   18 +
 test/t/enum/writer_test_cases.cpp                  |   30 +
 test/t/exceptions/test_cases.cpp                   |   27 +
 test/t/fixed32/data-max-uint.pbf                   |    1 +
 test/t/fixed32/data-min-uint.pbf                   |  Bin 0 -> 5 bytes
 test/t/fixed32/data-zero.pbf                       |  Bin 0 -> 5 bytes
 test/t/fixed32/test_cases.cpp                      |  102 +
 test/t/fixed32/testcase.cpp                        |   17 +
 test/t/fixed32/testcase.proto                      |   11 +
 test/t/fixed32/writer_test_cases.cpp               |   38 +
 test/t/fixed64/data-max-uint.pbf                   |    1 +
 test/t/fixed64/data-min-uint.pbf                   |  Bin 0 -> 9 bytes
 test/t/fixed64/data-zero.pbf                       |  Bin 0 -> 9 bytes
 test/t/fixed64/test_cases.cpp                      |   69 +
 test/t/fixed64/testcase.cpp                        |   17 +
 test/t/fixed64/testcase.proto                      |   11 +
 test/t/float/data-neg.pbf                          |    1 +
 test/t/float/data-pos.pbf                          |    1 +
 test/t/float/data-zero.pbf                         |  Bin 0 -> 5 bytes
 test/t/float/test_cases.cpp                        |   69 +
 test/t/float/testcase.cpp                          |   17 +
 test/t/float/testcase.proto                        |   11 +
 test/t/int32/data-max.pbf                          |    1 +
 test/t/int32/data-min.pbf                          |    1 +
 test/t/int32/data-neg.pbf                          |    1 +
 test/t/int32/data-pos.pbf                          |    1 +
 test/t/int32/data-zero.pbf                         |  Bin 0 -> 2 bytes
 test/t/int32/test_cases.cpp                        |  119 +
 test/t/int32/testcase.cpp                          |   23 +
 test/t/int32/testcase.proto                        |   11 +
 test/t/int32/writer_test_cases.cpp                 |   54 +
 test/t/int64/data-max.pbf                          |    1 +
 test/t/int64/data-min.pbf                          |    1 +
 test/t/int64/data-neg.pbf                          |    1 +
 test/t/int64/data-overflow.pbf                     |    1 +
 test/t/int64/data-pos.pbf                          |    1 +
 test/t/int64/data-zero.pbf                         |  Bin 0 -> 2 bytes
 test/t/int64/test_cases.cpp                        |  108 +
 test/t/int64/testcase.cpp                          |   33 +
 test/t/int64/testcase.proto                        |   11 +
 test/t/message/data-message.pbf                    |    3 +
 test/t/message/data-opt-element.pbf                |    2 +
 test/t/message/data-opt-empty.pbf                  |    0
 test/t/message/test_cases.cpp                      |   88 +
 test/t/message/testcase.cpp                        |   19 +
 test/t/message/testcase.proto                      |   17 +
 test/t/message/writer_test_cases.cpp               |   29 +
 test/t/nested/data-message.pbf                     |    5 +
 test/t/nested/data-no-message.pbf                  |  Bin 0 -> 4 bytes
 test/t/nested/test_cases.cpp                       |  163 +
 test/t/nested/testcase.cpp                         |   21 +
 test/t/nested/testcase.proto                       |   20 +
 test/t/nested/writer_test_cases.cpp                |   46 +
 test/t/repeated/data-empty.pbf                     |    0
 test/t/repeated/data-many.pbf                      |  Bin 0 -> 32 bytes
 test/t/repeated/data-one.pbf                       |  Bin 0 -> 2 bytes
 test/t/repeated/test_cases.cpp                     |   80 +
 test/t/repeated/testcase.cpp                       |   19 +
 test/t/repeated/testcase.proto                     |   11 +
 test/t/repeated/writer_test_cases.cpp              |   40 +
 test/t/repeated_packed_bool/data-empty.pbf         |    0
 test/t/repeated_packed_bool/data-many.pbf          |  Bin 0 -> 6 bytes
 test/t/repeated_packed_bool/data-one.pbf           |    2 +
 test/t/repeated_packed_bool/test_cases.cpp         |   85 +
 test/t/repeated_packed_bool/testcase.cpp           |   18 +
 test/t/repeated_packed_bool/testcase.proto         |   11 +
 test/t/repeated_packed_double/data-empty.pbf       |    0
 test/t/repeated_packed_double/data-many.pbf        |  Bin 0 -> 42 bytes
 test/t/repeated_packed_double/data-one.pbf         |    3 +
 test/t/repeated_packed_double/test_cases.cpp       |   84 +
 test/t/repeated_packed_double/testcase.cpp         |   19 +
 test/t/repeated_packed_double/testcase.proto       |   11 +
 test/t/repeated_packed_enum/data-empty.pbf         |    0
 test/t/repeated_packed_enum/data-many.pbf          |  Bin 0 -> 5 bytes
 test/t/repeated_packed_enum/data-one.pbf           |  Bin 0 -> 3 bytes
 test/t/repeated_packed_enum/test_cases.cpp         |   84 +
 test/t/repeated_packed_enum/testcase.cpp           |   17 +
 test/t/repeated_packed_enum/testcase.proto         |   18 +
 test/t/repeated_packed_fixed32/data-empty.pbf      |    0
 test/t/repeated_packed_fixed32/data-many.pbf       |  Bin 0 -> 18 bytes
 test/t/repeated_packed_fixed32/data-one.pbf        |  Bin 0 -> 6 bytes
 test/t/repeated_packed_fixed32/test_cases.cpp      |  120 +
 test/t/repeated_packed_fixed32/testcase.cpp        |   18 +
 test/t/repeated_packed_fixed32/testcase.proto      |   11 +
 .../repeated_packed_fixed32/writer_test_cases.cpp  |   79 +
 test/t/repeated_packed_fixed64/data-empty.pbf      |    0
 test/t/repeated_packed_fixed64/data-many.pbf       |  Bin 0 -> 34 bytes
 test/t/repeated_packed_fixed64/data-one.pbf        |  Bin 0 -> 10 bytes
 test/t/repeated_packed_fixed64/test_cases.cpp      |   83 +
 test/t/repeated_packed_fixed64/testcase.cpp        |   18 +
 test/t/repeated_packed_fixed64/testcase.proto      |   11 +
 test/t/repeated_packed_float/data-empty.pbf        |    0
 test/t/repeated_packed_float/data-many.pbf         |  Bin 0 -> 22 bytes
 test/t/repeated_packed_float/data-one.pbf          |    2 +
 test/t/repeated_packed_float/test_cases.cpp        |   84 +
 test/t/repeated_packed_float/testcase.cpp          |   19 +
 test/t/repeated_packed_float/testcase.proto        |   11 +
 test/t/repeated_packed_int32/data-empty.pbf        |    0
 test/t/repeated_packed_int32/data-many.pbf         |  Bin 0 -> 30 bytes
 test/t/repeated_packed_int32/data-one.pbf          |    2 +
 test/t/repeated_packed_int32/test_cases.cpp        |   87 +
 test/t/repeated_packed_int32/testcase.cpp          |   20 +
 test/t/repeated_packed_int32/testcase.proto        |   11 +
 test/t/repeated_packed_int64/data-empty.pbf        |    0
 test/t/repeated_packed_int64/data-many.pbf         |  Bin 0 -> 34 bytes
 test/t/repeated_packed_int64/data-one.pbf          |    2 +
 test/t/repeated_packed_int64/test_cases.cpp        |   85 +
 test/t/repeated_packed_int64/testcase.cpp          |   20 +
 test/t/repeated_packed_int64/testcase.proto        |   11 +
 test/t/repeated_packed_sfixed32/data-empty.pbf     |    0
 test/t/repeated_packed_sfixed32/data-many.pbf      |  Bin 0 -> 26 bytes
 test/t/repeated_packed_sfixed32/data-one.pbf       |  Bin 0 -> 6 bytes
 test/t/repeated_packed_sfixed32/test_cases.cpp     |   86 +
 test/t/repeated_packed_sfixed32/testcase.cpp       |   20 +
 test/t/repeated_packed_sfixed32/testcase.proto     |   11 +
 test/t/repeated_packed_sfixed64/data-empty.pbf     |    0
 test/t/repeated_packed_sfixed64/data-many.pbf      |  Bin 0 -> 50 bytes
 test/t/repeated_packed_sfixed64/data-one.pbf       |  Bin 0 -> 10 bytes
 test/t/repeated_packed_sfixed64/test_cases.cpp     |   85 +
 test/t/repeated_packed_sfixed64/testcase.cpp       |   20 +
 test/t/repeated_packed_sfixed64/testcase.proto     |   11 +
 test/t/repeated_packed_sint32/data-empty.pbf       |    0
 test/t/repeated_packed_sint32/data-many.pbf        |  Bin 0 -> 16 bytes
 test/t/repeated_packed_sint32/data-one.pbf         |    2 +
 test/t/repeated_packed_sint32/test_cases.cpp       |   85 +
 test/t/repeated_packed_sint32/testcase.cpp         |   20 +
 test/t/repeated_packed_sint32/testcase.proto       |   11 +
 test/t/repeated_packed_sint64/data-empty.pbf       |    0
 test/t/repeated_packed_sint64/data-many.pbf        |  Bin 0 -> 26 bytes
 test/t/repeated_packed_sint64/data-one.pbf         |    2 +
 test/t/repeated_packed_sint64/test_cases.cpp       |   85 +
 test/t/repeated_packed_sint64/testcase.cpp         |   20 +
 test/t/repeated_packed_sint64/testcase.proto       |   11 +
 test/t/repeated_packed_uint32/data-empty.pbf       |    0
 test/t/repeated_packed_uint32/data-many.pbf        |  Bin 0 -> 10 bytes
 test/t/repeated_packed_uint32/data-one.pbf         |    2 +
 test/t/repeated_packed_uint32/test_cases.cpp       |   83 +
 test/t/repeated_packed_uint32/testcase.cpp         |   18 +
 test/t/repeated_packed_uint32/testcase.proto       |   11 +
 test/t/repeated_packed_uint64/data-empty.pbf       |    0
 test/t/repeated_packed_uint64/data-many.pbf        |  Bin 0 -> 15 bytes
 test/t/repeated_packed_uint64/data-one.pbf         |    2 +
 test/t/repeated_packed_uint64/test_cases.cpp       |   83 +
 test/t/repeated_packed_uint64/testcase.cpp         |   18 +
 test/t/repeated_packed_uint64/testcase.proto       |   11 +
 test/t/sfixed32/data-max-int.pbf                   |    1 +
 test/t/sfixed32/data-min-int.pbf                   |  Bin 0 -> 5 bytes
 test/t/sfixed32/data-zero.pbf                      |  Bin 0 -> 5 bytes
 test/t/sfixed32/test_cases.cpp                     |   69 +
 test/t/sfixed32/testcase.cpp                       |   17 +
 test/t/sfixed32/testcase.proto                     |   11 +
 test/t/sfixed64/data-max-int.pbf                   |    1 +
 test/t/sfixed64/data-min-int.pbf                   |  Bin 0 -> 9 bytes
 test/t/sfixed64/data-zero.pbf                      |  Bin 0 -> 9 bytes
 test/t/sfixed64/test_cases.cpp                     |   69 +
 test/t/sfixed64/testcase.cpp                       |   17 +
 test/t/sfixed64/testcase.proto                     |   11 +
 test/t/sint32/data-max.pbf                         |    1 +
 test/t/sint32/data-min.pbf                         |    1 +
 test/t/sint32/data-neg.pbf                         |    1 +
 test/t/sint32/data-pos.pbf                         |    1 +
 test/t/sint32/data-zero.pbf                        |  Bin 0 -> 2 bytes
 test/t/sint32/test_cases.cpp                       |   99 +
 test/t/sint32/testcase.cpp                         |   23 +
 test/t/sint32/testcase.proto                       |   11 +
 test/t/sint64/data-max.pbf                         |    1 +
 test/t/sint64/data-min.pbf                         |    1 +
 test/t/sint64/data-neg.pbf                         |    1 +
 test/t/sint64/data-pos.pbf                         |    1 +
 test/t/sint64/data-zero.pbf                        |  Bin 0 -> 2 bytes
 test/t/sint64/test_cases.cpp                       |   99 +
 test/t/sint64/testcase.cpp                         |   23 +
 test/t/sint64/testcase.proto                       |   11 +
 test/t/skip/test_cases.cpp                         |  141 +
 test/t/string/data-empty.pbf                       |  Bin 0 -> 2 bytes
 test/t/string/data-one.pbf                         |    2 +
 test/t/string/data-string.pbf                      |    2 +
 test/t/string/test_cases.cpp                       |   78 +
 test/t/string/testcase.cpp                         |   17 +
 test/t/string/testcase.proto                       |   11 +
 test/t/string/writer_test_cases.cpp                |   38 +
 test/t/tags/data-tag-1.pbf                         |    1 +
 test/t/tags/data-tag-200.pbf                       |    1 +
 test/t/tags/data-tag-200000.pbf                    |    1 +
 test/t/tags/data-tag-max.pbf                       |    1 +
 test/t/tags/test_cases.cpp                         |   59 +
 test/t/tags/testcase.cpp                           |   32 +
 test/t/tags/testcase.proto                         |   29 +
 test/t/uint32/data-max.pbf                         |    1 +
 test/t/uint32/data-pos.pbf                         |    1 +
 test/t/uint32/data-zero.pbf                        |  Bin 0 -> 2 bytes
 test/t/uint32/test_cases.cpp                       |   69 +
 test/t/uint32/testcase.cpp                         |   17 +
 test/t/uint32/testcase.proto                       |   11 +
 test/t/uint64/data-max.pbf                         |    1 +
 test/t/uint64/data-pos.pbf                         |    1 +
 test/t/uint64/data-zero.pbf                        |  Bin 0 -> 2 bytes
 test/t/uint64/test_cases.cpp                       |   69 +
 test/t/uint64/testcase.cpp                         |   17 +
 test/t/uint64/testcase.proto                       |   11 +
 test/t/varint/test_cases.cpp                       |   55 +
 test/t/vector_tile/data.vector.pbf                 |  Bin 0 -> 412300 bytes
 test/t/vector_tile/test_cases.cpp                  |  112 +
 test/t/zigzag/test_cases.cpp                       |   51 +
 test/tests.cpp                                     |   24 +
 test/writer_tests.cpp                              |    4 +
 tutorial.md                                        |  400 +
 264 files changed, 20322 insertions(+)

diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..18430a8
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,14 @@
+coverage
+*.gcov
+out
+protozero.Makefile
+tests.target.mk
+
+#Visual Studio files and folders
+Release
+Debug
+deps
+*.sdf
+*.sln
+*.suo
+*.vcxproj*
diff --git a/.travis.yml b/.travis.yml
new file mode 100644
index 0000000..a525b14
--- /dev/null
+++ b/.travis.yml
@@ -0,0 +1,58 @@
+#-----------------------------------------------------------------------------
+#
+#  Configuration for continuous integration service at travis-ci.org
+#
+#-----------------------------------------------------------------------------
+
+language: cpp
+
+sudo: false
+
+matrix:
+    include:
+        - os: linux
+          compiler: clang
+          env: INSTALL_CXX=clang++-3.5
+          addons:
+            apt:
+                sources: ['ubuntu-toolchain-r-test', 'llvm-toolchain-precise-3.5']
+                packages: ['clang-3.5','libprotobuf-dev','protobuf-compiler']
+        - os: linux
+          compiler: clang
+          env: INSTALL_CXX=clang++-3.6
+          #env: INSTALL_CXX=clang++-3.6 COVERAGE=llvm-cov-3.6
+          addons:
+            apt:
+                sources: ['ubuntu-toolchain-r-test', 'llvm-toolchain-precise-3.6']
+                packages: ['clang-3.6','libprotobuf-dev','protobuf-compiler']
+        - os: linux
+          compiler: gcc
+          env: INSTALL_CXX=g++-4.8
+          addons:
+            apt:
+                sources: ['ubuntu-toolchain-r-test']
+                packages: ['g++-4.8','libprotobuf-dev','protobuf-compiler']
+        - os: linux
+          compiler: gcc
+          env: INSTALL_CXX=g++-4.9 COVERAGE=gcov-4.9
+          addons:
+            apt:
+                sources: ['ubuntu-toolchain-r-test']
+                packages: ['g++-4.9','libprotobuf-dev','protobuf-compiler']
+        - os: osx
+          compiler: clang
+
+before_script:
+    - if [ -n "${INSTALL_CXX}" ]; then CXX=${INSTALL_CXX}; fi
+    - if [[ $(uname -s) == 'Darwin' ]]; then brew install protobuf; fi;
+    - make test
+
+script:
+    - if [ -n "${COVERAGE}" ]; then
+        make clean;
+        CXXFLAGS="--coverage" LDFLAGS="--coverage" make test;
+        ${COVERAGE} -lp test/*tests.o test/t/*/*test_cases.o;
+        pip install --user cpp-coveralls;
+        ~/.local/bin/coveralls --no-gcov -i include/protozero;
+      fi
+
diff --git a/LICENSE.from_folly b/LICENSE.from_folly
new file mode 100644
index 0000000..f433b1a
--- /dev/null
+++ b/LICENSE.from_folly
@@ -0,0 +1,177 @@
+
+                                 Apache License
+                           Version 2.0, January 2004
+                        http://www.apache.org/licenses/
+
+   TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+
+   1. Definitions.
+
+      "License" shall mean the terms and conditions for use, reproduction,
+      and distribution as defined by Sections 1 through 9 of this document.
+
+      "Licensor" shall mean the copyright owner or entity authorized by
+      the copyright owner that is granting the License.
+
+      "Legal Entity" shall mean the union of the acting entity and all
+      other entities that control, are controlled by, or are under common
+      control with that entity. For the purposes of this definition,
+      "control" means (i) the power, direct or indirect, to cause the
+      direction or management of such entity, whether by contract or
+      otherwise, or (ii) ownership of fifty percent (50%) or more of the
+      outstanding shares, or (iii) beneficial ownership of such entity.
+
+      "You" (or "Your") shall mean an individual or Legal Entity
+      exercising permissions granted by this License.
+
+      "Source" form shall mean the preferred form for making modifications,
+      including but not limited to software source code, documentation
+      source, and configuration files.
+
+      "Object" form shall mean any form resulting from mechanical
+      transformation or translation of a Source form, including but
+      not limited to compiled object code, generated documentation,
+      and conversions to other media types.
+
+      "Work" shall mean the work of authorship, whether in Source or
+      Object form, made available under the License, as indicated by a
+      copyright notice that is included in or attached to the work
+      (an example is provided in the Appendix below).
+
+      "Derivative Works" shall mean any work, whether in Source or Object
+      form, that is based on (or derived from) the Work and for which the
+      editorial revisions, annotations, elaborations, or other modifications
+      represent, as a whole, an original work of authorship. For the purposes
+      of this License, Derivative Works shall not include works that remain
+      separable from, or merely link (or bind by name) to the interfaces of,
+      the Work and Derivative Works thereof.
+
+      "Contribution" shall mean any work of authorship, including
+      the original version of the Work and any modifications or additions
+      to that Work or Derivative Works thereof, that is intentionally
+      submitted to Licensor for inclusion in the Work by the copyright owner
+      or by an individual or Legal Entity authorized to submit on behalf of
+      the copyright owner. For the purposes of this definition, "submitted"
+      means any form of electronic, verbal, or written communication sent
+      to the Licensor or its representatives, including but not limited to
+      communication on electronic mailing lists, source code control systems,
+      and issue tracking systems that are managed by, or on behalf of, the
+      Licensor for the purpose of discussing and improving the Work, but
+      excluding communication that is conspicuously marked or otherwise
+      designated in writing by the copyright owner as "Not a Contribution."
+
+      "Contributor" shall mean Licensor and any individual or Legal Entity
+      on behalf of whom a Contribution has been received by Licensor and
+      subsequently incorporated within the Work.
+
+   2. Grant of Copyright License. Subject to the terms and conditions of
+      this License, each Contributor hereby grants to You a perpetual,
+      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+      copyright license to reproduce, prepare Derivative Works of,
+      publicly display, publicly perform, sublicense, and distribute the
+      Work and such Derivative Works in Source or Object form.
+
+   3. Grant of Patent License. Subject to the terms and conditions of
+      this License, each Contributor hereby grants to You a perpetual,
+      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+      (except as stated in this section) patent license to make, have made,
+      use, offer to sell, sell, import, and otherwise transfer the Work,
+      where such license applies only to those patent claims licensable
+      by such Contributor that are necessarily infringed by their
+      Contribution(s) alone or by combination of their Contribution(s)
+      with the Work to which such Contribution(s) was submitted. If You
+      institute patent litigation against any entity (including a
+      cross-claim or counterclaim in a lawsuit) alleging that the Work
+      or a Contribution incorporated within the Work constitutes direct
+      or contributory patent infringement, then any patent licenses
+      granted to You under this License for that Work shall terminate
+      as of the date such litigation is filed.
+
+   4. Redistribution. You may reproduce and distribute copies of the
+      Work or Derivative Works thereof in any medium, with or without
+      modifications, and in Source or Object form, provided that You
+      meet the following conditions:
+
+      (a) You must give any other recipients of the Work or
+          Derivative Works a copy of this License; and
+
+      (b) You must cause any modified files to carry prominent notices
+          stating that You changed the files; and
+
+      (c) You must retain, in the Source form of any Derivative Works
+          that You distribute, all copyright, patent, trademark, and
+          attribution notices from the Source form of the Work,
+          excluding those notices that do not pertain to any part of
+          the Derivative Works; and
+
+      (d) If the Work includes a "NOTICE" text file as part of its
+          distribution, then any Derivative Works that You distribute must
+          include a readable copy of the attribution notices contained
+          within such NOTICE file, excluding those notices that do not
+          pertain to any part of the Derivative Works, in at least one
+          of the following places: within a NOTICE text file distributed
+          as part of the Derivative Works; within the Source form or
+          documentation, if provided along with the Derivative Works; or,
+          within a display generated by the Derivative Works, if and
+          wherever such third-party notices normally appear. The contents
+          of the NOTICE file are for informational purposes only and
+          do not modify the License. You may add Your own attribution
+          notices within Derivative Works that You distribute, alongside
+          or as an addendum to the NOTICE text from the Work, provided
+          that such additional attribution notices cannot be construed
+          as modifying the License.
+
+      You may add Your own copyright statement to Your modifications and
+      may provide additional or different license terms and conditions
+      for use, reproduction, or distribution of Your modifications, or
+      for any such Derivative Works as a whole, provided Your use,
+      reproduction, and distribution of the Work otherwise complies with
+      the conditions stated in this License.
+
+   5. Submission of Contributions. Unless You explicitly state otherwise,
+      any Contribution intentionally submitted for inclusion in the Work
+      by You to the Licensor shall be under the terms and conditions of
+      this License, without any additional terms or conditions.
+      Notwithstanding the above, nothing herein shall supersede or modify
+      the terms of any separate license agreement you may have executed
+      with Licensor regarding such Contributions.
+
+   6. Trademarks. This License does not grant permission to use the trade
+      names, trademarks, service marks, or product names of the Licensor,
+      except as required for reasonable and customary use in describing the
+      origin of the Work and reproducing the content of the NOTICE file.
+
+   7. Disclaimer of Warranty. Unless required by applicable law or
+      agreed to in writing, Licensor provides the Work (and each
+      Contributor provides its Contributions) on an "AS IS" BASIS,
+      WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+      implied, including, without limitation, any warranties or conditions
+      of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
+      PARTICULAR PURPOSE. You are solely responsible for determining the
+      appropriateness of using or redistributing the Work and assume any
+      risks associated with Your exercise of permissions under this License.
+
+   8. Limitation of Liability. In no event and under no legal theory,
+      whether in tort (including negligence), contract, or otherwise,
+      unless required by applicable law (such as deliberate and grossly
+      negligent acts) or agreed to in writing, shall any Contributor be
+      liable to You for damages, including any direct, indirect, special,
+      incidental, or consequential damages of any character arising as a
+      result of this License or out of the use or inability to use the
+      Work (including but not limited to damages for loss of goodwill,
+      work stoppage, computer failure or malfunction, or any and all
+      other commercial damages or losses), even if such Contributor
+      has been advised of the possibility of such damages.
+
+   9. Accepting Warranty or Additional Liability. While redistributing
+      the Work or Derivative Works thereof, You may choose to offer,
+      and charge a fee for, acceptance of support, warranty, indemnity,
+      or other liability obligations and/or rights consistent with this
+      License. However, in accepting such obligations, You may act only
+      on Your own behalf and on Your sole responsibility, not on behalf
+      of any other Contributor, and only if You agree to indemnify,
+      defend, and hold each Contributor harmless for any liability
+      incurred by, or claims asserted against, such Contributor by reason
+      of your accepting any such warranty or additional liability.
+
+   END OF TERMS AND CONDITIONS
diff --git a/LICENSE.md b/LICENSE.md
new file mode 100644
index 0000000..d0b3011
--- /dev/null
+++ b/LICENSE.md
@@ -0,0 +1,24 @@
+protozero copyright (c) Mapbox.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are
+met:
+
+    * Redistributions of source code must retain the above copyright
+      notice, this list of conditions and the following disclaimer.
+    * Redistributions in binary form must reproduce the above copyright
+      notice, this list of conditions and the following disclaimer in
+      the documentation and/or other materials provided with the
+      distribution.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
+IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
diff --git a/Makefile b/Makefile
new file mode 100644
index 0000000..fb1774a
--- /dev/null
+++ b/Makefile
@@ -0,0 +1,109 @@
+# first inherit from env
+CXX := $(CXX)
+CXXFLAGS := $(CXXFLAGS)
+LDFLAGS := $(LDFLAGS)
+
+WARNING_FLAGS := -Wall -Wextra -pedantic -Wsign-compare -Wsign-conversion -Wunused-parameter -Wno-float-equal
+
+ifneq ($(findstring clang,$(CXX)),)
+    WARNING_FLAGS += -Wno-reserved-id-macro -Weverything -Wno-weak-vtables -Wno-c++98-compat -Wno-c++98-compat-pedantic -Wno-exit-time-destructors -Wno-switch-enum -Wno-padded
+endif
+
+COMMON_FLAGS := -fvisibility-inlines-hidden -std=c++11 $(WARNING_FLAGS)
+
+RELEASE_FLAGS := -O3 -DNDEBUG -march=native
+DEBUG_FLAGS := -O0 -g -fno-inline-functions
+
+OS:=$(shell uname -s)
+ifeq ($(OS),Darwin)
+    CXXFLAGS += -stdlib=libc++
+    LDFLAGS += -stdlib=libc++
+endif
+
+TEST_CASES := $(wildcard test/t/*/test_cases.cpp)
+TEST_CASES_O := $(subst .cpp,.o,$(TEST_CASES))
+
+WRITER_TEST_CASES := $(wildcard test/t/*/writer_test_cases.cpp)
+WRITER_TEST_CASES_O := $(subst .cpp,.o,$(WRITER_TEST_CASES))
+
+PROTO_FILES := $(subst writer_test_cases.cpp,testcase.proto,$(WRITER_TEST_CASES))
+PROTO_FILES_CC := $(subst .proto,.pb.cc,$(PROTO_FILES))
+PROTO_FILES_H := $(subst .proto,.pb.h,$(PROTO_FILES))
+PROTO_FILES_O := $(subst .proto,.pb.o,$(PROTO_FILES))
+
+HPP_FILES := include/protozero/exception.hpp \
+             include/protozero/varint.hpp \
+             include/protozero/pbf_types.hpp \
+             include/protozero/pbf_reader.hpp \
+             include/protozero/pbf_writer.hpp
+
+CFLAGS_PROTOBUF := $(shell pkg-config protobuf --cflags)
+LDFLAGS_PROTOBUF := $(shell pkg-config protobuf --libs-only-L)
+
+all: ./test/tests test/writer_tests
+
+./test/t/%/test_cases.o: test/t/%/test_cases.cpp $(HPP_FILES)
+	$(CXX) -c -Iinclude -Itest/include $(CXXFLAGS) $(COMMON_FLAGS) $(DEBUG_FLAGS) $< -o $@
+
+./test/tests.o: test/tests.cpp $(HPP_FILES)
+	$(CXX) -c -Iinclude -Itest/include $(CXXFLAGS) $(COMMON_FLAGS) $(DEBUG_FLAGS) $< -o $@
+
+./test/tests: test/tests.o $(TEST_CASES_O)
+	$(CXX) $(LDFLAGS) $^ -o $@
+
+./test/t/%/testcase.pb.cc: ./test/t/%/testcase.proto
+	protoc --cpp_out=. $^
+
+./test/t/%/testcase.pb.o: ./test/t/%/testcase.pb.cc
+	$(CXX) -c -I. -Iinclude -Itest/include $(CXXFLAGS) $(CFLAGS_PROTOBUF) -std=c++11 $(DEBUG_FLAGS) $< -o $@
+
+./test/t/%/writer_test_cases.o: ./test/t/%/writer_test_cases.cpp $(HPP_FILES)
+	$(CXX) -c -I. -Iinclude -Itest/include $(CXXFLAGS) $(CFLAGS_PROTOBUF) $(COMMON_FLAGS) $(DEBUG_FLAGS) $< -o $@
+
+./test/writer_tests.o: test/writer_tests.cpp $(HPP_FILES) $(PROTO_FILES_CC)
+	$(CXX) -c -I. -Iinclude -Itest/include $(CXXFLAGS) $(COMMON_FLAGS) $(DEBUG_FLAGS) $< -o $@
+
+./test/writer_tests: test/writer_tests.o $(PROTO_FILES_O) $(WRITER_TEST_CASES_O)
+	$(CXX) $(LDFLAGS) $(LDFLAGS_PROTOBUF) $^ -lprotobuf-lite -pthread -o $@
+
+test: all
+	./test/tests
+	./test/writer_tests
+
+iwyu: $(HPP_FILES) test/tests.cpp test/writer_tests.cpp
+	iwyu -Xiwyu -- -std=c++11 -Iinclude include/protozero/exception.hpp || true
+	iwyu -Xiwyu -- -std=c++11 -Iinclude include/protozero/varint.hpp || true
+	iwyu -Xiwyu -- -std=c++11 -Iinclude include/protozero/pbf_types.hpp || true
+	iwyu -Xiwyu -- -std=c++11 -Iinclude include/protozero/pbf_reader.hpp || true
+	iwyu -Xiwyu -- -std=c++11 -Iinclude include/protozero/pbf_writer.hpp || true
+	iwyu -Xiwyu -- -std=c++11 -Iinclude -Itest/include test/tests.cpp || true
+	iwyu -Xiwyu -- -std=c++11 -Iinclude -Itest/include test/writer_tests.cpp || true
+
+check: $(HPP_FILES) test/tests.cpp test/include/test.hpp test/include/testcase.hpp test/t/*/testcase.cpp $(TEST_CASES)
+	cppcheck -Uassert --std=c++11 --enable=all --suppress=incorrectStringBooleanError $^
+
+doc: doc/Doxyfile README.md tutorial.md $(HPP_FILES)
+	doxygen doc/Doxyfile
+
+clean:
+	rm -f ./test/tests
+	rm -f ./test/tests.o
+	rm -f ./test/tests.gc*
+	rm -f ./test/writer_tests
+	rm -f ./test/writer_tests.o
+	rm -f ./test/writer_tests.gc*
+	rm -f ./test/t/*/testcase.pb.cc
+	rm -f ./test/t/*/testcase.pb.h
+	rm -f ./test/t/*/testcase.pb.o
+	rm -f ./test/t/*/testcase.pb.gc*
+	rm -f ./test/t/*/testcase.o
+	rm -f ./test/t/*/testcase
+	rm -f ./test/t/*/test_cases.o
+	rm -f ./test/t/*/test_cases.gc*
+	rm -f ./test/t/*/writer_test_cases.o
+	rm -f ./test/t/*/writer_test_cases.gc*
+	rm -f ./*.gcov
+	rm -fr doc/doxygen_sqlite3.db doc/html coverage
+
+.PHONY: all test iwyu check doc
+
diff --git a/README.md b/README.md
new file mode 100644
index 0000000..db4c994
--- /dev/null
+++ b/README.md
@@ -0,0 +1,115 @@
+# protozero
+
+Minimalistic protocol buffer decoder and encoder in C++.
+
+Designed for high performance. Suitable for writing zero copy parsers and
+encoders with minimal need for run-time allocation of memory.
+
+Low-level: this is designed to be a building block for writing a very
+customized decoder for a stable protobuf schema. If your protobuf schema
+is changing frequently or lazy decoding is not critical for your application
+then this approach offers no value: just use the decoding API available via the
+C++ API that can be generated via the Google Protobufs `protoc` program.
+
+[![Travis Build Status](https://travis-ci.org/mapbox/protozero.svg?branch=master)](https://travis-ci.org/mapbox/protozero)
+[![Appveyor Build Status](https://ci.appveyor.com/api/projects/status/o354pq10y96mnr6d?svg=true)](https://ci.appveyor.com/project/Mapbox/protozero)
+[![Coverage Status](https://coveralls.io/repos/mapbox/protozero/badge.svg?branch=master&service=github)](https://coveralls.io/github/mapbox/protozero?branch=master)
+
+
+## Depends
+
+ - C++11 compiler
+ - A working knowledge of how
+   [protocol buffer encoding works](https://developers.google.com/protocol-buffers/docs/encoding).
+
+
+## How it works
+
+The protozero code does **not** read `.proto` files used by the usual Protobuf
+implementations. The developer using protozero has to manually "translate" the
+`.proto` description into code. This means there is no way to access any of the
+information from the `.proto` description. This results in a few restrictions:
+
+* The names of the fields are not available.
+* Enum names are not available, you'll have to use the values they are defined
+  with.
+* Default values are not available.
+* Field types have to be hardcoded. The library does not know which types to
+  expect, so the user of the library has to supply the right types. Some checks
+  are made using `assert()`, but mostly the user has to take care of that.
+
+The library will make sure not to overrun the buffer it was given, but
+basically all other checks have to be made in user code!
+
+See the [tutorial](tutorial.md) for more information on how to use it.
+
+Call `make doc` to build the Doxygen documentation. (You'll need
+[Doxygen](http://www.stack.nl/~dimitri/doxygen/) installed.) Then open
+`doc/html/index.html` in your browser to read it.
+
+
+## Limitations
+
+* The current implementation does not support big-endian machines. Fixed sized
+  integers and floats/doubles will not decode properly.
+* A protobuf message has to fit into memory completely, otherwise it can not
+  be parsed with this library. There is no streaming support.
+* The length of a string, bytes, or submessage can't be more than 2^31-1.
+* The Google Protobuf spec documents that a non-repeated field can actually
+  appear several times in a message and the implementation is required to
+  return the value of the last version of that field in this case.
+  `pbf_reader.hpp` does not enforce this. If this feature is needed in your
+  case, you have to do this yourself.
+* There is no specific support for maps but they can be used as described in
+  the "Backwards compatibility" section of
+  https://developers.google.com/protocol-buffers/docs/proto3#maps.
+
+
+## Tests
+
+Extensive tests are included. Call
+
+    make test
+
+to build all tests and run them.
+
+See `test/README.md` for more details about the test.
+
+You can also use `gyp` to build the reader tests:
+
+    gyp --depth=. --build=Release
+    ./out/Release/tests
+
+This will clobber the `Makefile` from the repository! Instead of `Release` you
+can use `Debug` for a debug build.
+
+
+## Coverage report
+
+To get a coverage report compile and link with `--coverage`:
+
+    CXXFLAGS="--coverage" LDFLAGS="--coverage" make test
+
+If you are using `g++` use `gcov` to generate a report (results are in `*.gcov`
+files):
+
+    gcov -lp test/*tests.o test/t/*/*test_cases.o
+
+If you are using `clang++` use `llvm-cov` instead:
+
+    llvm-cov gcov -lp test/*tests.o test/t/*/*test_cases.o
+
+If you are using `g++` you can use `gcovr` to generate nice HTML output:
+
+    mkdir -p coverage
+    gcovr -r . --html --html-details -o coverage/index.html
+
+Open `coverage/index.html` in your browser to see the report.
+
+
+## Cppcheck
+
+For extra checks with [Cppcheck](http://cppcheck.sourceforge.net/) you can call
+
+    make check
+
diff --git a/appveyor.yml b/appveyor.yml
new file mode 100644
index 0000000..4e13aa9
--- /dev/null
+++ b/appveyor.yml
@@ -0,0 +1,25 @@
+os: Visual Studio 2015 RC
+
+platform:
+  - x64
+  - x86
+
+configuration:
+  - Debug
+  - Release
+
+install:
+  - SET PATH=c:\python27;%PATH%
+  - SET PATH=C:\Program Files (x86)\MSBuild\14.0\bin\;%PATH%
+  - git clone --quiet --depth 1 https://chromium.googlesource.com/external/gyp.git deps/gyp
+  # note windows requires --generator-output to be absolute
+  - python deps/gyp/gyp_main.py protozero.gyp --depth=. -f msvs -G msvs_version=2015
+  - set MSBUILD_PLATFORM=%platform%
+  - if "%MSBUILD_PLATFORM%" == "x86" set MSBUILD_PLATFORM=Win32
+  - msbuild protozero.sln /nologo /p:Configuration=%configuration%;Platform=%MSBUILD_PLATFORM%
+  - .\%configuration%\tests.exe
+
+build: off
+test: off
+deploy: off
+#test
diff --git a/common.gypi b/common.gypi
new file mode 100644
index 0000000..aa24ac6
--- /dev/null
+++ b/common.gypi
@@ -0,0 +1,143 @@
+{
+  "conditions": [
+    ["OS=='win'", {
+          "target_defaults": {
+            "default_configuration": "Release_x64",
+            #"msbuild_toolset":"CTP_Nov2013",
+            "msvs_settings": {
+              "VCCLCompilerTool": {
+                "ExceptionHandling": 1, # /EHsc
+                "RuntimeTypeInfo": "true" # /GR
+              }
+            },
+            "configurations": {
+              "Debug_Win32": {
+                "msvs_configuration_platform": "Win32",
+                "defines": [ "DEBUG","_DEBUG"],
+                "msvs_settings": {
+                  "VCCLCompilerTool": {
+                    "RuntimeLibrary": "1", # static debug /MTd
+                    "Optimization": 0, # /Od, no optimization
+                    "MinimalRebuild": "false",
+                    "OmitFramePointers": "false",
+                    "BasicRuntimeChecks": 3 # /RTC1
+                  }
+                }
+              },
+              "Debug_x64": {
+                "msvs_configuration_platform": "x64",
+                "defines": [ "DEBUG","_DEBUG"],
+                "msvs_settings": {
+                  "VCCLCompilerTool": {
+                    "RuntimeLibrary": "1", # static debug /MTd
+                    "Optimization": 0, # /Od, no optimization
+                    "MinimalRebuild": "false",
+                    "OmitFramePointers": "false",
+                    "BasicRuntimeChecks": 3 # /RTC1
+                  }
+                }
+              },
+              "Release_Win32": {
+                "msvs_configuration_platform": "Win32",
+                "defines": [ "NDEBUG"],
+                "msvs_settings": {
+                  "VCCLCompilerTool": {
+                    "RuntimeLibrary": 0, # static release
+                    "Optimization": 3, # /Ox, full optimization
+                    "FavorSizeOrSpeed": 1, # /Ot, favour speed over size
+                    "InlineFunctionExpansion": 2, # /Ob2, inline anything eligible
+                    "WholeProgramOptimization": "true", # /GL, whole program optimization, needed for LTCG
+                    "OmitFramePointers": "true",
+                    "EnableFunctionLevelLinking": "true",
+                    "EnableIntrinsicFunctions": "true",
+                    #"AdditionalOptions": [
+                    #  "/MP", # compile across multiple CPUs
+                    #],
+                    "DebugInformationFormat": "0"
+                  },
+                  "VCLibrarianTool": {
+                    "AdditionalOptions": [
+                      "/LTCG" # link time code generation
+                    ],
+                  },
+                  "VCLinkerTool": {
+                    "LinkTimeCodeGeneration": 1, # link-time code generation
+                    "OptimizeReferences": 2, # /OPT:REF
+                    "EnableCOMDATFolding": 2, # /OPT:ICF
+                    "LinkIncremental": 1, # disable incremental linking
+                    "GenerateDebugInformation": "false"
+                  }
+                }
+              },
+              "Release_x64": {
+                "msvs_configuration_platform": "x64",
+                "defines": [ "NDEBUG"],
+                "msvs_settings": {
+                  "VCCLCompilerTool": {
+                    "RuntimeLibrary": 0, # static release
+                    "Optimization": 3, # /Ox, full optimization
+                    "FavorSizeOrSpeed": 1, # /Ot, favour speed over size
+                    "InlineFunctionExpansion": 2, # /Ob2, inline anything eligible
+                    "WholeProgramOptimization": "true", # /GL, whole program optimization, needed for LTCG
+                    "OmitFramePointers": "true",
+                    "EnableFunctionLevelLinking": "true",
+                    "EnableIntrinsicFunctions": "true",
+                    #"AdditionalOptions": [
+                    #  "/MP", # compile across multiple CPUs
+                    #],
+                    "DebugInformationFormat": "0"
+                  },
+                  "VCLibrarianTool": {
+                    "AdditionalOptions": [
+                      "/LTCG" # link time code generation
+                    ],
+                  },
+                  "VCLinkerTool": {
+                    "LinkTimeCodeGeneration": 1, # link-time code generation
+                    "OptimizeReferences": 2, # /OPT:REF
+                    "EnableCOMDATFolding": 2, # /OPT:ICF
+                    "LinkIncremental": 1, # disable incremental linking
+                    "GenerateDebugInformation": "false"
+                  }
+                }
+              }
+            }
+          }
+    }, {
+        "target_defaults": {
+            "default_configuration": "Release",
+            "xcode_settings": {
+              "CLANG_CXX_LIBRARY": "libc++",
+              "CLANG_CXX_LANGUAGE_STANDARD":"c++11",
+              "GCC_VERSION": "com.apple.compilers.llvm.clang.1_0",
+            },
+            "cflags_cc": ["-std=c++11"],
+            "configurations": {
+                "Debug": {
+                    "defines": [
+                        "DEBUG"
+                    ],
+                    "xcode_settings": {
+                        "GCC_OPTIMIZATION_LEVEL": "0",
+                        "GCC_GENERATE_DEBUGGING_SYMBOLS": "YES",
+                        "OTHER_CPLUSPLUSFLAGS": [ "-Wall", "-Wextra", "-pedantic", "-g", "-O0" ]
+                    }
+                },
+                "Release": {
+                    "defines": [
+                        "NDEBUG"
+                    ],
+                    "xcode_settings": {
+                        "GCC_OPTIMIZATION_LEVEL": "3",
+                        "GCC_GENERATE_DEBUGGING_SYMBOLS": "NO",
+                        "DEAD_CODE_STRIPPING": "YES",
+                        "GCC_INLINES_ARE_PRIVATE_EXTERN": "YES",
+                        "OTHER_CPLUSPLUSFLAGS": [ "-Wall", "-Wextra", "-pedantic", "-O3" ]
+                    }
+                }
+            }
+        }
+    }]
+  ]
+}
+
diff --git a/doc/.gitignore b/doc/.gitignore
new file mode 100644
index 0000000..03c8987
--- /dev/null
+++ b/doc/.gitignore
@@ -0,0 +1,2 @@
+doxygen_sqlite3.db
+html
diff --git a/doc/Doxyfile b/doc/Doxyfile
new file mode 100644
index 0000000..14e0311
--- /dev/null
+++ b/doc/Doxyfile
@@ -0,0 +1,2352 @@
+# Doxyfile 1.8.8
+
+# This file describes the settings to be used by the documentation system
+# doxygen (www.doxygen.org) for a project.
+#
+# All text after a double hash (##) is considered a comment and is placed in
+# front of the TAG it is preceding.
+#
+# All text after a single hash (#) is considered a comment and will be ignored.
+# The format is:
+# TAG = value [value, ...]
+# For lists, items can also be appended using:
+# TAG += value [value, ...]
+# Values that contain spaces should be placed between quotes (\" \").
+
+#---------------------------------------------------------------------------
+# Project related configuration options
+#---------------------------------------------------------------------------
+
+# This tag specifies the encoding used for all characters in the config file
+# that follow. The default is UTF-8 which is also the encoding used for all text
+# before the first occurrence of this tag. Doxygen uses libiconv (or the iconv
+# built into libc) for the transcoding. See http://www.gnu.org/software/libiconv
+# for the list of possible encodings.
+# The default value is: UTF-8.
+
+DOXYFILE_ENCODING      = UTF-8
+
+# The PROJECT_NAME tag is a single word (or a sequence of words surrounded by
+# double-quotes, unless you are using Doxywizard) that should identify the
+# project for which the documentation is generated. This name is used in the
+# title of most generated pages and in a few other places.
+# The default value is: My Project.
+
+PROJECT_NAME           = "protozero"
+
+# The PROJECT_NUMBER tag can be used to enter a project or revision number. This
+# could be handy for archiving the generated documentation or if some version
+# control system is used.
+
+PROJECT_NUMBER         =
+
+# Using the PROJECT_BRIEF tag one can provide an optional one line description
+# for a project that appears at the top of each page and should give viewer a
+# quick idea about the purpose of the project. Keep the description short.
+
+PROJECT_BRIEF          = "Minimalistic protocol buffer decoder and encoder in C++."
+
+# With the PROJECT_LOGO tag one can specify an logo or icon that is included in
+# the documentation. The maximum height of the logo should not exceed 55 pixels
+# and the maximum width should not exceed 200 pixels. Doxygen will copy the logo
+# to the output directory.
+
+PROJECT_LOGO           =
+
+# The OUTPUT_DIRECTORY tag is used to specify the (relative or absolute) path
+# into which the generated documentation will be written. If a relative path is
+# entered, it will be relative to the location where doxygen was started. If
+# left blank the current directory will be used.
+
+OUTPUT_DIRECTORY       = doc
+
+# If the CREATE_SUBDIRS tag is set to YES, then doxygen will create 4096 sub-
+# directories (in 2 levels) under the output directory of each output format and
+# will distribute the generated files over these directories. Enabling this
+# option can be useful when feeding doxygen a huge amount of source files, where
+# putting all generated files in the same directory would otherwise causes
+# performance problems for the file system.
+# The default value is: NO.
+
+CREATE_SUBDIRS         = NO
+
+# If the ALLOW_UNICODE_NAMES tag is set to YES, doxygen will allow non-ASCII
+# characters to appear in the names of generated files. If set to NO, non-ASCII
+# characters will be escaped, for example _xE3_x81_x84 will be used for Unicode
+# U+3044.
+# The default value is: NO.
+
+ALLOW_UNICODE_NAMES    = NO
+
+# The OUTPUT_LANGUAGE tag is used to specify the language in which all
+# documentation generated by doxygen is written. Doxygen will use this
+# information to generate all constant output in the proper language.
+# Possible values are: Afrikaans, Arabic, Armenian, Brazilian, Catalan, Chinese,
+# Chinese-Traditional, Croatian, Czech, Danish, Dutch, English (United States),
+# Esperanto, Farsi (Persian), Finnish, French, German, Greek, Hungarian,
+# Indonesian, Italian, Japanese, Japanese-en (Japanese with English messages),
+# Korean, Korean-en (Korean with English messages), Latvian, Lithuanian,
+# Macedonian, Norwegian, Persian (Farsi), Polish, Portuguese, Romanian, Russian,
+# Serbian, Serbian-Cyrillic, Slovak, Slovene, Spanish, Swedish, Turkish,
+# Ukrainian and Vietnamese.
+# The default value is: English.
+
+OUTPUT_LANGUAGE        = English
+
+# If the BRIEF_MEMBER_DESC tag is set to YES doxygen will include brief member
+# descriptions after the members that are listed in the file and class
+# documentation (similar to Javadoc). Set to NO to disable this.
+# The default value is: YES.
+
+BRIEF_MEMBER_DESC      = YES
+
+# If the REPEAT_BRIEF tag is set to YES doxygen will prepend the brief
+# description of a member or function before the detailed description
+#
+# Note: If both HIDE_UNDOC_MEMBERS and BRIEF_MEMBER_DESC are set to NO, the
+# brief descriptions will be completely suppressed.
+# The default value is: YES.
+
+REPEAT_BRIEF           = YES
+
+# This tag implements a quasi-intelligent brief description abbreviator that is
+# used to form the text in various listings. Each string in this list, if found
+# as the leading text of the brief description, will be stripped from the text
+# and the result, after processing the whole list, is used as the annotated
+# text. Otherwise, the brief description is used as-is. If left blank, the
+# following values are used ($name is automatically replaced with the name of
+# the entity):The $name class, The $name widget, The $name file, is, provides,
+# specifies, contains, represents, a, an and the.
+
+ABBREVIATE_BRIEF       =
+
+# If the ALWAYS_DETAILED_SEC and REPEAT_BRIEF tags are both set to YES then
+# doxygen will generate a detailed section even if there is only a brief
+# description.
+# The default value is: NO.
+
+ALWAYS_DETAILED_SEC    = NO
+
+# If the INLINE_INHERITED_MEMB tag is set to YES, doxygen will show all
+# inherited members of a class in the documentation of that class as if those
+# members were ordinary class members. Constructors, destructors and assignment
+# operators of the base classes will not be shown.
+# The default value is: NO.
+
+INLINE_INHERITED_MEMB  = NO
+
+# If the FULL_PATH_NAMES tag is set to YES doxygen will prepend the full path
+# before files name in the file list and in the header files. If set to NO the
+# shortest path that makes the file name unique will be used
+# The default value is: YES.
+
+FULL_PATH_NAMES        = YES
+
+# The STRIP_FROM_PATH tag can be used to strip a user-defined part of the path.
+# Stripping is only done if one of the specified strings matches the left-hand
+# part of the path. The tag can be used to show relative paths in the file list.
+# If left blank the directory from which doxygen is run is used as the path to
+# strip.
+#
+# Note that you can specify absolute paths here, but also relative paths, which
+# will be relative from the directory where doxygen is started.
+# This tag requires that the tag FULL_PATH_NAMES is set to YES.
+
+STRIP_FROM_PATH        =
+
+# The STRIP_FROM_INC_PATH tag can be used to strip a user-defined part of the
+# path mentioned in the documentation of a class, which tells the reader which
+# header file to include in order to use a class. If left blank only the name of
+# the header file containing the class definition is used. Otherwise one should
+# specify the list of include paths that are normally passed to the compiler
+# using the -I flag.
+
+STRIP_FROM_INC_PATH    =
+
+# If the SHORT_NAMES tag is set to YES, doxygen will generate much shorter (but
+# less readable) file names. This can be useful is your file systems doesn't
+# support long names like on DOS, Mac, or CD-ROM.
+# The default value is: NO.
+
+SHORT_NAMES            = NO
+
+# If the JAVADOC_AUTOBRIEF tag is set to YES then doxygen will interpret the
+# first line (until the first dot) of a Javadoc-style comment as the brief
+# description. If set to NO, the Javadoc-style will behave just like regular Qt-
+# style comments (thus requiring an explicit @brief command for a brief
+# description.)
+# The default value is: NO.
+
+JAVADOC_AUTOBRIEF      = NO
+
+# If the QT_AUTOBRIEF tag is set to YES then doxygen will interpret the first
+# line (until the first dot) of a Qt-style comment as the brief description. If
+# set to NO, the Qt-style will behave just like regular Qt-style comments (thus
+# requiring an explicit \brief command for a brief description.)
+# The default value is: NO.
+
+QT_AUTOBRIEF           = NO
+
+# The MULTILINE_CPP_IS_BRIEF tag can be set to YES to make doxygen treat a
+# multi-line C++ special comment block (i.e. a block of //! or /// comments) as
+# a brief description. This used to be the default behavior. The new default is
+# to treat a multi-line C++ comment block as a detailed description. Set this
+# tag to YES if you prefer the old behavior instead.
+#
+# Note that setting this tag to YES also means that rational rose comments are
+# not recognized any more.
+# The default value is: NO.
+
+MULTILINE_CPP_IS_BRIEF = NO
+
+# If the INHERIT_DOCS tag is set to YES then an undocumented member inherits the
+# documentation from any documented member that it re-implements.
+# The default value is: YES.
+
+INHERIT_DOCS           = YES
+
+# If the SEPARATE_MEMBER_PAGES tag is set to YES, then doxygen will produce a
+# new page for each member. If set to NO, the documentation of a member will be
+# part of the file/class/namespace that contains it.
+# The default value is: NO.
+
+SEPARATE_MEMBER_PAGES  = NO
+
+# The TAB_SIZE tag can be used to set the number of spaces in a tab. Doxygen
+# uses this value to replace tabs by spaces in code fragments.
+# Minimum value: 1, maximum value: 16, default value: 4.
+
+TAB_SIZE               = 4
+
+# This tag can be used to specify a number of aliases that act as commands in
+# the documentation. An alias has the form:
+# name=value
+# For example adding
+# "sideeffect=@par Side Effects:\n"
+# will allow you to put the command \sideeffect (or @sideeffect) in the
+# documentation, which will result in a user-defined paragraph with heading
+# "Side Effects:". You can put \n's in the value part of an alias to insert
+# newlines.
+
+ALIASES                =
+
+# This tag can be used to specify a number of word-keyword mappings (TCL only).
+# A mapping has the form "name=value". For example adding "class=itcl::class"
+# will allow you to use the command class in the itcl::class meaning.
+
+TCL_SUBST              =
+
+# Set the OPTIMIZE_OUTPUT_FOR_C tag to YES if your project consists of C sources
+# only. Doxygen will then generate output that is more tailored for C. For
+# instance, some of the names that are used will be different. The list of all
+# members will be omitted, etc.
+# The default value is: NO.
+
+OPTIMIZE_OUTPUT_FOR_C  = NO
+
+# Set the OPTIMIZE_OUTPUT_JAVA tag to YES if your project consists of Java or
+# Python sources only. Doxygen will then generate output that is more tailored
+# for that language. For instance, namespaces will be presented as packages,
+# qualified scopes will look different, etc.
+# The default value is: NO.
+
+OPTIMIZE_OUTPUT_JAVA   = NO
+
+# Set the OPTIMIZE_FOR_FORTRAN tag to YES if your project consists of Fortran
+# sources. Doxygen will then generate output that is tailored for Fortran.
+# The default value is: NO.
+
+OPTIMIZE_FOR_FORTRAN   = NO
+
+# Set the OPTIMIZE_OUTPUT_VHDL tag to YES if your project consists of VHDL
+# sources. Doxygen will then generate output that is tailored for VHDL.
+# The default value is: NO.
+
+OPTIMIZE_OUTPUT_VHDL   = NO
+
+# Doxygen selects the parser to use depending on the extension of the files it
+# parses. With this tag you can assign which parser to use for a given
+# extension. Doxygen has a built-in mapping, but you can override or extend it
+# using this tag. The format is ext=language, where ext is a file extension, and
+# language is one of the parsers supported by doxygen: IDL, Java, Javascript,
+# C#, C, C++, D, PHP, Objective-C, Python, Fortran (fixed format Fortran:
+# FortranFixed, free formatted Fortran: FortranFree, unknown formatted Fortran:
+# Fortran. In the later case the parser tries to guess whether the code is fixed
+# or free formatted code, this is the default for Fortran type files), VHDL. For
+# instance to make doxygen treat .inc files as Fortran files (default is PHP),
+# and .f files as C (default is Fortran), use: inc=Fortran f=C.
+#
+# Note For files without extension you can use no_extension as a placeholder.
+#
+# Note that for custom extensions you also need to set FILE_PATTERNS otherwise
+# the files are not read by doxygen.
+
+EXTENSION_MAPPING      =
+
+# If the MARKDOWN_SUPPORT tag is enabled then doxygen pre-processes all comments
+# according to the Markdown format, which allows for more readable
+# documentation. See http://daringfireball.net/projects/markdown/ for details.
+# The output of markdown processing is further processed by doxygen, so you can
+# mix doxygen, HTML, and XML commands with Markdown formatting. Disable only in
+# case of backward compatibilities issues.
+# The default value is: YES.
+
+MARKDOWN_SUPPORT       = YES
+
+# When enabled doxygen tries to link words that correspond to documented
+# classes, or namespaces to their corresponding documentation. Such a link can
+# be prevented in individual cases by by putting a % sign in front of the word
+# or globally by setting AUTOLINK_SUPPORT to NO.
+# The default value is: YES.
+
+AUTOLINK_SUPPORT       = YES
+
+# If you use STL classes (i.e. std::string, std::vector, etc.) but do not want
+# to include (a tag file for) the STL sources as input, then you should set this
+# tag to YES in order to let doxygen match functions declarations and
+# definitions whose arguments contain STL classes (e.g. func(std::string);
+# versus func(std::string) {}). This also make the inheritance and collaboration
+# diagrams that involve STL classes more complete and accurate.
+# The default value is: NO.
+
+BUILTIN_STL_SUPPORT    = NO
+
+# If you use Microsoft's C++/CLI language, you should set this option to YES to
+# enable parsing support.
+# The default value is: NO.
+
+CPP_CLI_SUPPORT        = NO
+
+# Set the SIP_SUPPORT tag to YES if your project consists of sip (see:
+# http://www.riverbankcomputing.co.uk/software/sip/intro) sources only. Doxygen
+# will parse them like normal C++ but will assume all classes use public instead
+# of private inheritance when no explicit protection keyword is present.
+# The default value is: NO.
+
+SIP_SUPPORT            = NO
+
+# For Microsoft's IDL there are propget and propput attributes to indicate
+# getter and setter methods for a property. Setting this option to YES will make
+# doxygen to replace the get and set methods by a property in the documentation.
+# This will only work if the methods are indeed getting or setting a simple
+# type. If this is not the case, or you want to show the methods anyway, you
+# should set this option to NO.
+# The default value is: YES.
+
+IDL_PROPERTY_SUPPORT   = YES
+
+# If member grouping is used in the documentation and the DISTRIBUTE_GROUP_DOC
+# tag is set to YES, then doxygen will reuse the documentation of the first
+# member in the group (if any) for the other members of the group. By default
+# all members of a group must be documented explicitly.
+# The default value is: NO.
+
+DISTRIBUTE_GROUP_DOC   = NO
+
+# Set the SUBGROUPING tag to YES to allow class member groups of the same type
+# (for instance a group of public functions) to be put as a subgroup of that
+# type (e.g. under the Public Functions section). Set it to NO to prevent
+# subgrouping. Alternatively, this can be done per class using the
+# \nosubgrouping command.
+# The default value is: YES.
+
+SUBGROUPING            = YES
+
+# When the INLINE_GROUPED_CLASSES tag is set to YES, classes, structs and unions
+# are shown inside the group in which they are included (e.g. using \ingroup)
+# instead of on a separate page (for HTML and Man pages) or section (for LaTeX
+# and RTF).
+#
+# Note that this feature does not work in combination with
+# SEPARATE_MEMBER_PAGES.
+# The default value is: NO.
+
+INLINE_GROUPED_CLASSES = NO
+
+# When the INLINE_SIMPLE_STRUCTS tag is set to YES, structs, classes, and unions
+# with only public data fields or simple typedef fields will be shown inline in
+# the documentation of the scope in which they are defined (i.e. file,
+# namespace, or group documentation), provided this scope is documented. If set
+# to NO, structs, classes, and unions are shown on a separate page (for HTML and
+# Man pages) or section (for LaTeX and RTF).
+# The default value is: NO.
+
+INLINE_SIMPLE_STRUCTS  = NO
+
+# When TYPEDEF_HIDES_STRUCT tag is enabled, a typedef of a struct, union, or
+# enum is documented as struct, union, or enum with the name of the typedef. So
+# typedef struct TypeS {} TypeT, will appear in the documentation as a struct
+# with name TypeT. When disabled the typedef will appear as a member of a file,
+# namespace, or class. And the struct will be named TypeS. This can typically be
+# useful for C code in case the coding convention dictates that all compound
+# types are typedef'ed and only the typedef is referenced, never the tag name.
+# The default value is: NO.
+
+TYPEDEF_HIDES_STRUCT   = NO
+
+# The size of the symbol lookup cache can be set using LOOKUP_CACHE_SIZE. This
+# cache is used to resolve symbols given their name and scope. Since this can be
+# an expensive process and often the same symbol appears multiple times in the
+# code, doxygen keeps a cache of pre-resolved symbols. If the cache is too small
+# doxygen will become slower. If the cache is too large, memory is wasted. The
+# cache size is given by this formula: 2^(16+LOOKUP_CACHE_SIZE). The valid range
+# is 0..9, the default is 0, corresponding to a cache size of 2^16=65536
+# symbols. At the end of a run doxygen will report the cache usage and suggest
+# the optimal cache size from a speed point of view.
+# Minimum value: 0, maximum value: 9, default value: 0.
+
+LOOKUP_CACHE_SIZE      = 0
+
+#---------------------------------------------------------------------------
+# Build related configuration options
+#---------------------------------------------------------------------------
+
+# If the EXTRACT_ALL tag is set to YES doxygen will assume all entities in
+# documentation are documented, even if no documentation was available. Private
+# class members and static file members will be hidden unless the
+# EXTRACT_PRIVATE respectively EXTRACT_STATIC tags are set to YES.
+# Note: This will also disable the warnings about undocumented members that are
+# normally produced when WARNINGS is set to YES.
+# The default value is: NO.
+
+EXTRACT_ALL            = NO
+
+# If the EXTRACT_PRIVATE tag is set to YES all private members of a class will
+# be included in the documentation.
+# The default value is: NO.
+
+EXTRACT_PRIVATE        = NO
+
+# If the EXTRACT_PACKAGE tag is set to YES all members with package or internal
+# scope will be included in the documentation.
+# The default value is: NO.
+
+EXTRACT_PACKAGE        = NO
+
+# If the EXTRACT_STATIC tag is set to YES all static members of a file will be
+# included in the documentation.
+# The default value is: NO.
+
+EXTRACT_STATIC         = NO
+
+# If the EXTRACT_LOCAL_CLASSES tag is set to YES classes (and structs) defined
+# locally in source files will be included in the documentation. If set to NO
+# only classes defined in header files are included. Does not have any effect
+# for Java sources.
+# The default value is: YES.
+
+EXTRACT_LOCAL_CLASSES  = YES
+
+# This flag is only useful for Objective-C code. When set to YES local methods,
+# which are defined in the implementation section but not in the interface are
+# included in the documentation. If set to NO only methods in the interface are
+# included.
+# The default value is: NO.
+
+EXTRACT_LOCAL_METHODS  = NO
+
+# If this flag is set to YES, the members of anonymous namespaces will be
+# extracted and appear in the documentation as a namespace called
+# 'anonymous_namespace{file}', where file will be replaced with the base name of
+# the file that contains the anonymous namespace. By default anonymous namespace
+# are hidden.
+# The default value is: NO.
+
+EXTRACT_ANON_NSPACES   = NO
+
+# If the HIDE_UNDOC_MEMBERS tag is set to YES, doxygen will hide all
+# undocumented members inside documented classes or files. If set to NO these
+# members will be included in the various overviews, but no documentation
+# section is generated. This option has no effect if EXTRACT_ALL is enabled.
+# The default value is: NO.
+
+HIDE_UNDOC_MEMBERS     = NO
+
+# If the HIDE_UNDOC_CLASSES tag is set to YES, doxygen will hide all
+# undocumented classes that are normally visible in the class hierarchy. If set
+# to NO these classes will be included in the various overviews. This option has
+# no effect if EXTRACT_ALL is enabled.
+# The default value is: NO.
+
+HIDE_UNDOC_CLASSES     = NO
+
+# If the HIDE_FRIEND_COMPOUNDS tag is set to YES, doxygen will hide all friend
+# (class|struct|union) declarations. If set to NO these declarations will be
+# included in the documentation.
+# The default value is: NO.
+
+HIDE_FRIEND_COMPOUNDS  = NO
+
+# If the HIDE_IN_BODY_DOCS tag is set to YES, doxygen will hide any
+# documentation blocks found inside the body of a function. If set to NO these
+# blocks will be appended to the function's detailed documentation block.
+# The default value is: NO.
+
+HIDE_IN_BODY_DOCS      = NO
+
+# The INTERNAL_DOCS tag determines if documentation that is typed after a
+# \internal command is included. If the tag is set to NO then the documentation
+# will be excluded. Set it to YES to include the internal documentation.
+# The default value is: NO.
+
+INTERNAL_DOCS          = NO
+
+# If the CASE_SENSE_NAMES tag is set to NO then doxygen will only generate file
+# names in lower-case letters. If set to YES upper-case letters are also
+# allowed. This is useful if you have classes or files whose names only differ
+# in case and if your file system supports case sensitive file names. Windows
+# and Mac users are advised to set this option to NO.
+# The default value is: system dependent.
+
+CASE_SENSE_NAMES       = YES
+
+# If the HIDE_SCOPE_NAMES tag is set to NO then doxygen will show members with
+# their full class and namespace scopes in the documentation. If set to YES the
+# scope will be hidden.
+# The default value is: NO.
+
+HIDE_SCOPE_NAMES       = NO
+
+# If the SHOW_INCLUDE_FILES tag is set to YES then doxygen will put a list of
+# the files that are included by a file in the documentation of that file.
+# The default value is: YES.
+
+SHOW_INCLUDE_FILES     = YES
+
+# If the SHOW_GROUPED_MEMB_INC tag is set to YES then Doxygen will add for each
+# grouped member an include statement to the documentation, telling the reader
+# which file to include in order to use the member.
+# The default value is: NO.
+
+SHOW_GROUPED_MEMB_INC  = NO
+
+# If the FORCE_LOCAL_INCLUDES tag is set to YES then doxygen will list include
+# files with double quotes in the documentation rather than with sharp brackets.
+# The default value is: NO.
+
+FORCE_LOCAL_INCLUDES   = NO
+
+# If the INLINE_INFO tag is set to YES then a tag [inline] is inserted in the
+# documentation for inline members.
+# The default value is: YES.
+
+INLINE_INFO            = YES
+
+# If the SORT_MEMBER_DOCS tag is set to YES then doxygen will sort the
+# (detailed) documentation of file and class members alphabetically by member
+# name. If set to NO the members will appear in declaration order.
+# The default value is: YES.
+
+SORT_MEMBER_DOCS       = YES
+
+# If the SORT_BRIEF_DOCS tag is set to YES then doxygen will sort the brief
+# descriptions of file, namespace and class members alphabetically by member
+# name. If set to NO the members will appear in declaration order. Note that
+# this will also influence the order of the classes in the class list.
+# The default value is: NO.
+
+SORT_BRIEF_DOCS        = NO
+
+# If the SORT_MEMBERS_CTORS_1ST tag is set to YES then doxygen will sort the
+# (brief and detailed) documentation of class members so that constructors and
+# destructors are listed first. If set to NO the constructors will appear in the
+# respective orders defined by SORT_BRIEF_DOCS and SORT_MEMBER_DOCS.
+# Note: If SORT_BRIEF_DOCS is set to NO this option is ignored for sorting brief
+# member documentation.
+# Note: If SORT_MEMBER_DOCS is set to NO this option is ignored for sorting
+# detailed member documentation.
+# The default value is: NO.
+
+SORT_MEMBERS_CTORS_1ST = NO
+
+# If the SORT_GROUP_NAMES tag is set to YES then doxygen will sort the hierarchy
+# of group names into alphabetical order. If set to NO the group names will
+# appear in their defined order.
+# The default value is: NO.
+
+SORT_GROUP_NAMES       = NO
+
+# If the SORT_BY_SCOPE_NAME tag is set to YES, the class list will be sorted by
+# fully-qualified names, including namespaces. If set to NO, the class list will
+# be sorted only by class name, not including the namespace part.
+# Note: This option is not very useful if HIDE_SCOPE_NAMES is set to YES.
+# Note: This option applies only to the class list, not to the alphabetical
+# list.
+# The default value is: NO.
+
+SORT_BY_SCOPE_NAME     = NO
+
+# If the STRICT_PROTO_MATCHING option is enabled and doxygen fails to do proper
+# type resolution of all parameters of a function it will reject a match between
+# the prototype and the implementation of a member function even if there is
+# only one candidate or it is obvious which candidate to choose by doing a
+# simple string match. By disabling STRICT_PROTO_MATCHING doxygen will still
+# accept a match between prototype and implementation in such cases.
+# The default value is: NO.
+
+STRICT_PROTO_MATCHING  = NO
+
+# The GENERATE_TODOLIST tag can be used to enable ( YES) or disable ( NO) the
+# todo list. This list is created by putting \todo commands in the
+# documentation.
+# The default value is: YES.
+
+GENERATE_TODOLIST      = YES
+
+# The GENERATE_TESTLIST tag can be used to enable ( YES) or disable ( NO) the
+# test list. This list is created by putting \test commands in the
+# documentation.
+# The default value is: YES.
+
+GENERATE_TESTLIST      = YES
+
+# The GENERATE_BUGLIST tag can be used to enable ( YES) or disable ( NO) the bug
+# list. This list is created by putting \bug commands in the documentation.
+# The default value is: YES.
+
+GENERATE_BUGLIST       = YES
+
+# The GENERATE_DEPRECATEDLIST tag can be used to enable ( YES) or disable ( NO)
+# the deprecated list. This list is created by putting \deprecated commands in
+# the documentation.
+# The default value is: YES.
+
+GENERATE_DEPRECATEDLIST= YES
+
+# The ENABLED_SECTIONS tag can be used to enable conditional documentation
+# sections, marked by \if <section_label> ... \endif and \cond <section_label>
+# ... \endcond blocks.
+
+ENABLED_SECTIONS       =
+
+# The MAX_INITIALIZER_LINES tag determines the maximum number of lines that the
+# initial value of a variable or macro / define can have for it to appear in the
+# documentation. If the initializer consists of more lines than specified here
+# it will be hidden. Use a value of 0 to hide initializers completely. The
+# appearance of the value of individual variables and macros / defines can be
+# controlled using \showinitializer or \hideinitializer command in the
+# documentation regardless of this setting.
+# Minimum value: 0, maximum value: 10000, default value: 30.
+
+MAX_INITIALIZER_LINES  = 30
+
+# Set the SHOW_USED_FILES tag to NO to disable the list of files generated at
+# the bottom of the documentation of classes and structs. If set to YES the list
+# will mention the files that were used to generate the documentation.
+# The default value is: YES.
+
+SHOW_USED_FILES        = YES
+
+# Set the SHOW_FILES tag to NO to disable the generation of the Files page. This
+# will remove the Files entry from the Quick Index and from the Folder Tree View
+# (if specified).
+# The default value is: YES.
+
+SHOW_FILES             = YES
+
+# Set the SHOW_NAMESPACES tag to NO to disable the generation of the Namespaces
+# page. This will remove the Namespaces entry from the Quick Index and from the
+# Folder Tree View (if specified).
+# The default value is: YES.
+
+SHOW_NAMESPACES        = YES
+
+# The FILE_VERSION_FILTER tag can be used to specify a program or script that
+# doxygen should invoke to get the current version for each file (typically from
+# the version control system). Doxygen will invoke the program by executing (via
+# popen()) the command command input-file, where command is the value of the
+# FILE_VERSION_FILTER tag, and input-file is the name of an input file provided
+# by doxygen. Whatever the program writes to standard output is used as the file
+# version. For an example see the documentation.
+
+FILE_VERSION_FILTER    =
+
+# The LAYOUT_FILE tag can be used to specify a layout file which will be parsed
+# by doxygen. The layout file controls the global structure of the generated
+# output files in an output format independent way. To create the layout file
+# that represents doxygen's defaults, run doxygen with the -l option. You can
+# optionally specify a file name after the option, if omitted DoxygenLayout.xml
+# will be used as the name of the layout file.
+#
+# Note that if you run doxygen from a directory containing a file called
+# DoxygenLayout.xml, doxygen will parse it automatically even if the LAYOUT_FILE
+# tag is left empty.
+
+LAYOUT_FILE            =
+
+# The CITE_BIB_FILES tag can be used to specify one or more bib files containing
+# the reference definitions. This must be a list of .bib files. The .bib
+# extension is automatically appended if omitted. This requires the bibtex tool
+# to be installed. See also http://en.wikipedia.org/wiki/BibTeX for more info.
+# For LaTeX the style of the bibliography can be controlled using
+# LATEX_BIB_STYLE. To use this feature you need bibtex and perl available in the
+# search path. See also \cite for info how to create references.
+
+CITE_BIB_FILES         =
+
+#---------------------------------------------------------------------------
+# Configuration options related to warning and progress messages
+#---------------------------------------------------------------------------
+
+# The QUIET tag can be used to turn on/off the messages that are generated to
+# standard output by doxygen. If QUIET is set to YES this implies that the
+# messages are off.
+# The default value is: NO.
+
+QUIET                  = YES
+
+# The WARNINGS tag can be used to turn on/off the warning messages that are
+# generated to standard error ( stderr) by doxygen. If WARNINGS is set to YES
+# this implies that the warnings are on.
+#
+# Tip: Turn warnings on while writing the documentation.
+# The default value is: YES.
+
+WARNINGS               = YES
+
+# If the WARN_IF_UNDOCUMENTED tag is set to YES, then doxygen will generate
+# warnings for undocumented members. If EXTRACT_ALL is set to YES then this flag
+# will automatically be disabled.
+# The default value is: YES.
+
+WARN_IF_UNDOCUMENTED   = YES
+
+# If the WARN_IF_DOC_ERROR tag is set to YES, doxygen will generate warnings for
+# potential errors in the documentation, such as not documenting some parameters
+# in a documented function, or documenting parameters that don't exist or using
+# markup commands wrongly.
+# The default value is: YES.
+
+WARN_IF_DOC_ERROR      = YES
+
+# This WARN_NO_PARAMDOC option can be enabled to get warnings for functions that
+# are documented, but have no documentation for their parameters or return
+# value. If set to NO doxygen will only warn about wrong or incomplete parameter
+# documentation, but not about the absence of documentation.
+# The default value is: NO.
+
+WARN_NO_PARAMDOC       = NO
+
+# The WARN_FORMAT tag determines the format of the warning messages that doxygen
+# can produce. The string should contain the $file, $line, and $text tags, which
+# will be replaced by the file and line number from which the warning originated
+# and the warning text. Optionally the format may contain $version, which will
+# be replaced by the version of the file (if it could be obtained via
+# FILE_VERSION_FILTER)
+# The default value is: $file:$line: $text.
+
+WARN_FORMAT            = "$file:$line: $text"
+
+# The WARN_LOGFILE tag can be used to specify a file to which warning and error
+# messages should be written. If left blank the output is written to standard
+# error (stderr).
+
+WARN_LOGFILE           =
+
+#---------------------------------------------------------------------------
+# Configuration options related to the input files
+#---------------------------------------------------------------------------
+
+# The INPUT tag is used to specify the files and/or directories that contain
+# documented source files. You may enter file names like myfile.cpp or
+# directories like /usr/src/myproject. Separate the files or directories with
+# spaces.
+# Note: If this tag is empty the current directory is searched.
+
+INPUT                  = README.md tutorial.md include/protozero
+
+# This tag can be used to specify the character encoding of the source files
+# that doxygen parses. Internally doxygen uses the UTF-8 encoding. Doxygen uses
+# libiconv (or the iconv built into libc) for the transcoding. See the libiconv
+# documentation (see: http://www.gnu.org/software/libiconv) for the list of
+# possible encodings.
+# The default value is: UTF-8.
+
+INPUT_ENCODING         = UTF-8
+
+# If the value of the INPUT tag contains directories, you can use the
+# FILE_PATTERNS tag to specify one or more wildcard patterns (like *.cpp and
+# *.h) to filter out the source-files in the directories. If left blank the
+# following patterns are tested:*.c, *.cc, *.cxx, *.cpp, *.c++, *.java, *.ii,
+# *.ixx, *.ipp, *.i++, *.inl, *.idl, *.ddl, *.odl, *.h, *.hh, *.hxx, *.hpp,
+# *.h++, *.cs, *.d, *.php, *.php4, *.php5, *.phtml, *.inc, *.m, *.markdown,
+# *.md, *.mm, *.dox, *.py, *.f90, *.f, *.for, *.tcl, *.vhd, *.vhdl, *.ucf,
+# *.qsf, *.as and *.js.
+
+FILE_PATTERNS          =
+
+# The RECURSIVE tag can be used to specify whether or not subdirectories should
+# be searched for input files as well.
+# The default value is: NO.
+
+RECURSIVE              = NO
+
+# The EXCLUDE tag can be used to specify files and/or directories that should be
+# excluded from the INPUT source files. This way you can easily exclude a
+# subdirectory from a directory tree whose root is specified with the INPUT tag.
+#
+# Note that relative paths are relative to the directory from which doxygen is
+# run.
+
+EXCLUDE                =
+
+# The EXCLUDE_SYMLINKS tag can be used to select whether or not files or
+# directories that are symbolic links (a Unix file system feature) are excluded
+# from the input.
+# The default value is: NO.
+
+EXCLUDE_SYMLINKS       = NO
+
+# If the value of the INPUT tag contains directories, you can use the
+# EXCLUDE_PATTERNS tag to specify one or more wildcard patterns to exclude
+# certain files from those directories.
+#
+# Note that the wildcards are matched against the file with absolute path, so to
+# exclude all test directories for example use the pattern */test/*
+
+EXCLUDE_PATTERNS       =
+
+# The EXCLUDE_SYMBOLS tag can be used to specify one or more symbol names
+# (namespaces, classes, functions, etc.) that should be excluded from the
+# output. The symbol name can be a fully qualified name, a word, or if the
+# wildcard * is used, a substring. Examples: ANamespace, AClass,
+# AClass::ANamespace, ANamespace::*Test
+#
+# Note that the wildcards are matched against the file with absolute path, so to
+# exclude all test directories use the pattern */test/*
+
+EXCLUDE_SYMBOLS        =
+
+# The EXAMPLE_PATH tag can be used to specify one or more files or directories
+# that contain example code fragments that are included (see the \include
+# command).
+
+EXAMPLE_PATH           =
+
+# If the value of the EXAMPLE_PATH tag contains directories, you can use the
+# EXAMPLE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp and
+# *.h) to filter out the source-files in the directories. If left blank all
+# files are included.
+
+EXAMPLE_PATTERNS       =
+
+# If the EXAMPLE_RECURSIVE tag is set to YES then subdirectories will be
+# searched for input files to be used with the \include or \dontinclude commands
+# irrespective of the value of the RECURSIVE tag.
+# The default value is: NO.
+
+EXAMPLE_RECURSIVE      = NO
+
+# The IMAGE_PATH tag can be used to specify one or more files or directories
+# that contain images that are to be included in the documentation (see the
+# \image command).
+
+IMAGE_PATH             =
+
+# The INPUT_FILTER tag can be used to specify a program that doxygen should
+# invoke to filter for each input file. Doxygen will invoke the filter program
+# by executing (via popen()) the command:
+#
+# <filter> <input-file>
+#
+# where <filter> is the value of the INPUT_FILTER tag, and <input-file> is the
+# name of an input file. Doxygen will then use the output that the filter
+# program writes to standard output. If FILTER_PATTERNS is specified, this tag
+# will be ignored.
+#
+# Note that the filter must not add or remove lines; it is applied before the
+# code is scanned, but not when the output code is generated. If lines are added
+# or removed, the anchors will not be placed correctly.
+
+INPUT_FILTER           =
+
+# The FILTER_PATTERNS tag can be used to specify filters on a per file pattern
+# basis. Doxygen will compare the file name with each pattern and apply the
+# filter if there is a match. The filters are a list of the form: pattern=filter
+# (like *.cpp=my_cpp_filter). See INPUT_FILTER for further information on how
+# filters are used. If the FILTER_PATTERNS tag is empty or if none of the
+# patterns match the file name, INPUT_FILTER is applied.
+
+FILTER_PATTERNS        =
+
+# If the FILTER_SOURCE_FILES tag is set to YES, the input filter (if set using
+# INPUT_FILTER ) will also be used to filter the input files that are used for
+# producing the source files to browse (i.e. when SOURCE_BROWSER is set to YES).
+# The default value is: NO.
+
+FILTER_SOURCE_FILES    = NO
+
+# The FILTER_SOURCE_PATTERNS tag can be used to specify source filters per file
+# pattern. A pattern will override the setting for FILTER_PATTERN (if any) and
+# it is also possible to disable source filtering for a specific pattern using
+# *.ext= (so without naming a filter).
+# This tag requires that the tag FILTER_SOURCE_FILES is set to YES.
+
+FILTER_SOURCE_PATTERNS =
+
+# If the USE_MDFILE_AS_MAINPAGE tag refers to the name of a markdown file that
+# is part of the input, its contents will be placed on the main page
+# (index.html). This can be useful if you have a project on for instance GitHub
+# and want to reuse the introduction page also for the doxygen output.
+
+USE_MDFILE_AS_MAINPAGE = README.md
+
+#---------------------------------------------------------------------------
+# Configuration options related to source browsing
+#---------------------------------------------------------------------------
+
+# If the SOURCE_BROWSER tag is set to YES then a list of source files will be
+# generated. Documented entities will be cross-referenced with these sources.
+#
+# Note: To get rid of all source code in the generated output, make sure that
+# also VERBATIM_HEADERS is set to NO.
+# The default value is: NO.
+
+SOURCE_BROWSER         = NO
+
+# Setting the INLINE_SOURCES tag to YES will include the body of functions,
+# classes and enums directly into the documentation.
+# The default value is: NO.
+
+INLINE_SOURCES         = NO
+
+# Setting the STRIP_CODE_COMMENTS tag to YES will instruct doxygen to hide any
+# special comment blocks from generated source code fragments. Normal C, C++ and
+# Fortran comments will always remain visible.
+# The default value is: YES.
+
+STRIP_CODE_COMMENTS    = YES
+
+# If the REFERENCED_BY_RELATION tag is set to YES then for each documented
+# function all documented functions referencing it will be listed.
+# The default value is: NO.
+
+REFERENCED_BY_RELATION = NO
+
+# If the REFERENCES_RELATION tag is set to YES then for each documented function
+# all documented entities called/used by that function will be listed.
+# The default value is: NO.
+
+REFERENCES_RELATION    = NO
+
+# If the REFERENCES_LINK_SOURCE tag is set to YES and SOURCE_BROWSER tag is set
+# to YES, then the hyperlinks from functions in REFERENCES_RELATION and
+# REFERENCED_BY_RELATION lists will link to the source code. Otherwise they will
+# link to the documentation.
+# The default value is: YES.
+
+REFERENCES_LINK_SOURCE = YES
+
+# If SOURCE_TOOLTIPS is enabled (the default) then hovering a hyperlink in the
+# source code will show a tooltip with additional information such as prototype,
+# brief description and links to the definition and documentation. Since this
+# will make the HTML file larger and loading of large files a bit slower, you
+# can opt to disable this feature.
+# The default value is: YES.
+# This tag requires that the tag SOURCE_BROWSER is set to YES.
+
+SOURCE_TOOLTIPS        = YES
+
+# If the USE_HTAGS tag is set to YES then the references to source code will
+# point to the HTML generated by the htags(1) tool instead of doxygen built-in
+# source browser. The htags tool is part of GNU's global source tagging system
+# (see http://www.gnu.org/software/global/global.html). You will need version
+# 4.8.6 or higher.
+#
+# To use it do the following:
+# - Install the latest version of global
+# - Enable SOURCE_BROWSER and USE_HTAGS in the config file
+# - Make sure the INPUT points to the root of the source tree
+# - Run doxygen as normal
+#
+# Doxygen will invoke htags (and that will in turn invoke gtags), so these
+# tools must be available from the command line (i.e. in the search path).
+#
+# The result: instead of the source browser generated by doxygen, the links to
+# source code will now point to the output of htags.
+# The default value is: NO.
+# This tag requires that the tag SOURCE_BROWSER is set to YES.
+
+USE_HTAGS              = NO
+
+# If the VERBATIM_HEADERS tag is set the YES then doxygen will generate a
+# verbatim copy of the header file for each class for which an include is
+# specified. Set to NO to disable this.
+# See also: Section \class.
+# The default value is: YES.
+
+VERBATIM_HEADERS       = YES
+
+# If the CLANG_ASSISTED_PARSING tag is set to YES, then doxygen will use the
+# clang parser (see: http://clang.llvm.org/) for more accurate parsing at the
+# cost of reduced performance. This can be particularly helpful with template
+# rich C++ code for which doxygen's built-in parser lacks the necessary type
+# information.
+# Note: The availability of this option depends on whether or not doxygen was
+# compiled with the --with-libclang option.
+# The default value is: NO.
+
+CLANG_ASSISTED_PARSING = NO
+
+# If clang assisted parsing is enabled you can provide the compiler with command
+# line options that you would normally use when invoking the compiler. Note that
+# the include paths will already be set by doxygen for the files and directories
+# specified with INPUT and INCLUDE_PATH.
+# This tag requires that the tag CLANG_ASSISTED_PARSING is set to YES.
+
+CLANG_OPTIONS          =
+
+#---------------------------------------------------------------------------
+# Configuration options related to the alphabetical class index
+#---------------------------------------------------------------------------
+
+# If the ALPHABETICAL_INDEX tag is set to YES, an alphabetical index of all
+# compounds will be generated. Enable this if the project contains a lot of
+# classes, structs, unions or interfaces.
+# The default value is: YES.
+
+ALPHABETICAL_INDEX     = YES
+
+# The COLS_IN_ALPHA_INDEX tag can be used to specify the number of columns in
+# which the alphabetical index list will be split.
+# Minimum value: 1, maximum value: 20, default value: 5.
+# This tag requires that the tag ALPHABETICAL_INDEX is set to YES.
+
+COLS_IN_ALPHA_INDEX    = 5
+
+# In case all classes in a project start with a common prefix, all classes will
+# be put under the same header in the alphabetical index. The IGNORE_PREFIX tag
+# can be used to specify a prefix (or a list of prefixes) that should be ignored
+# while generating the index headers.
+# This tag requires that the tag ALPHABETICAL_INDEX is set to YES.
+
+IGNORE_PREFIX          =
+
+#---------------------------------------------------------------------------
+# Configuration options related to the HTML output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_HTML tag is set to YES doxygen will generate HTML output
+# The default value is: YES.
+
+GENERATE_HTML          = YES
+
+# The HTML_OUTPUT tag is used to specify where the HTML docs will be put. If a
+# relative path is entered the value of OUTPUT_DIRECTORY will be put in front of
+# it.
+# The default directory is: html.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+HTML_OUTPUT            = html
+
+# The HTML_FILE_EXTENSION tag can be used to specify the file extension for each
+# generated HTML page (for example: .htm, .php, .asp).
+# The default value is: .html.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+HTML_FILE_EXTENSION    = .html
+
+# The HTML_HEADER tag can be used to specify a user-defined HTML header file for
+# each generated HTML page. If the tag is left blank doxygen will generate a
+# standard header.
+#
+# To get valid HTML the header file that includes any scripts and style sheets
+# that doxygen needs, which is dependent on the configuration options used (e.g.
+# the setting GENERATE_TREEVIEW). It is highly recommended to start with a
+# default header using
+# doxygen -w html new_header.html new_footer.html new_stylesheet.css
+# YourConfigFile
+# and then modify the file new_header.html. See also section "Doxygen usage"
+# for information on how to generate the default header that doxygen normally
+# uses.
+# Note: The header is subject to change so you typically have to regenerate the
+# default header when upgrading to a newer version of doxygen. For a description
+# of the possible markers and block names see the documentation.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+HTML_HEADER            =
+
+# The HTML_FOOTER tag can be used to specify a user-defined HTML footer for each
+# generated HTML page. If the tag is left blank doxygen will generate a standard
+# footer. See HTML_HEADER for more information on how to generate a default
+# footer and what special commands can be used inside the footer. See also
+# section "Doxygen usage" for information on how to generate the default footer
+# that doxygen normally uses.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+HTML_FOOTER            =
+
+# The HTML_STYLESHEET tag can be used to specify a user-defined cascading style
+# sheet that is used by each HTML page. It can be used to fine-tune the look of
+# the HTML output. If left blank doxygen will generate a default style sheet.
+# See also section "Doxygen usage" for information on how to generate the style
+# sheet that doxygen normally uses.
+# Note: It is recommended to use HTML_EXTRA_STYLESHEET instead of this tag, as
+# it is more robust and this tag (HTML_STYLESHEET) will in the future become
+# obsolete.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+HTML_STYLESHEET        =
+
+# The HTML_EXTRA_STYLESHEET tag can be used to specify additional user-defined
+# cascading style sheets that are included after the standard style sheets
+# created by doxygen. Using this option one can overrule certain style aspects.
+# This is preferred over using HTML_STYLESHEET since it does not replace the
+# standard style sheet and is therefor more robust against future updates.
+# Doxygen will copy the style sheet files to the output directory.
+# Note: The order of the extra stylesheet files is of importance (e.g. the last
+# stylesheet in the list overrules the setting of the previous ones in the
+# list). For an example see the documentation.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+HTML_EXTRA_STYLESHEET  =
+
+# The HTML_EXTRA_FILES tag can be used to specify one or more extra images or
+# other source files which should be copied to the HTML output directory. Note
+# that these files will be copied to the base HTML output directory. Use the
+# $relpath^ marker in the HTML_HEADER and/or HTML_FOOTER files to load these
+# files. In the HTML_STYLESHEET file, use the file name only. Also note that the
+# files will be copied as-is; there are no commands or markers available.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+HTML_EXTRA_FILES       =
+
+# The HTML_COLORSTYLE_HUE tag controls the color of the HTML output. Doxygen
+# will adjust the colors in the stylesheet and background images according to
+# this color. Hue is specified as an angle on a colorwheel, see
+# http://en.wikipedia.org/wiki/Hue for more information. For instance the value
+# 0 represents red, 60 is yellow, 120 is green, 180 is cyan, 240 is blue, 300
+# purple, and 360 is red again.
+# Minimum value: 0, maximum value: 359, default value: 220.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+HTML_COLORSTYLE_HUE    = 220
+
+# The HTML_COLORSTYLE_SAT tag controls the purity (or saturation) of the colors
+# in the HTML output. For a value of 0 the output will use grayscales only. A
+# value of 255 will produce the most vivid colors.
+# Minimum value: 0, maximum value: 255, default value: 100.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+HTML_COLORSTYLE_SAT    = 100
+
+# The HTML_COLORSTYLE_GAMMA tag controls the gamma correction applied to the
+# luminance component of the colors in the HTML output. Values below 100
+# gradually make the output lighter, whereas values above 100 make the output
+# darker. The value divided by 100 is the actual gamma applied, so 80 represents
+# a gamma of 0.8, The value 220 represents a gamma of 2.2, and 100 does not
+# change the gamma.
+# Minimum value: 40, maximum value: 240, default value: 80.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+HTML_COLORSTYLE_GAMMA  = 80
+
+# If the HTML_TIMESTAMP tag is set to YES then the footer of each generated HTML
+# page will contain the date and time when the page was generated. Setting this
+# to NO can help when comparing the output of multiple runs.
+# The default value is: YES.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+HTML_TIMESTAMP         = YES
+
+# If the HTML_DYNAMIC_SECTIONS tag is set to YES then the generated HTML
+# documentation will contain sections that can be hidden and shown after the
+# page has loaded.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+HTML_DYNAMIC_SECTIONS  = NO
+
+# With HTML_INDEX_NUM_ENTRIES one can control the preferred number of entries
+# shown in the various tree structured indices initially; the user can expand
+# and collapse entries dynamically later on. Doxygen will expand the tree to
+# such a level that at most the specified number of entries are visible (unless
+# a fully collapsed tree already exceeds this amount). So setting the number of
+# entries 1 will produce a full collapsed tree by default. 0 is a special value
+# representing an infinite number of entries and will result in a full expanded
+# tree by default.
+# Minimum value: 0, maximum value: 9999, default value: 100.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+HTML_INDEX_NUM_ENTRIES = 100
+
+# If the GENERATE_DOCSET tag is set to YES, additional index files will be
+# generated that can be used as input for Apple's Xcode 3 integrated development
+# environment (see: http://developer.apple.com/tools/xcode/), introduced with
+# OSX 10.5 (Leopard). To create a documentation set, doxygen will generate a
+# Makefile in the HTML output directory. Running make will produce the docset in
+# that directory and running make install will install the docset in
+# ~/Library/Developer/Shared/Documentation/DocSets so that Xcode will find it at
+# startup. See http://developer.apple.com/tools/creatingdocsetswithdoxygen.html
+# for more information.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+GENERATE_DOCSET        = NO
+
+# This tag determines the name of the docset feed. A documentation feed provides
+# an umbrella under which multiple documentation sets from a single provider
+# (such as a company or product suite) can be grouped.
+# The default value is: Doxygen generated docs.
+# This tag requires that the tag GENERATE_DOCSET is set to YES.
+
+DOCSET_FEEDNAME        = "Doxygen generated docs"
+
+# This tag specifies a string that should uniquely identify the documentation
+# set bundle. This should be a reverse domain-name style string, e.g.
+# com.mycompany.MyDocSet. Doxygen will append .docset to the name.
+# The default value is: org.doxygen.Project.
+# This tag requires that the tag GENERATE_DOCSET is set to YES.
+
+DOCSET_BUNDLE_ID       = org.doxygen.Project
+
+# The DOCSET_PUBLISHER_ID tag specifies a string that should uniquely identify
+# the documentation publisher. This should be a reverse domain-name style
+# string, e.g. com.mycompany.MyDocSet.documentation.
+# The default value is: org.doxygen.Publisher.
+# This tag requires that the tag GENERATE_DOCSET is set to YES.
+
+DOCSET_PUBLISHER_ID    = org.doxygen.Publisher
+
+# The DOCSET_PUBLISHER_NAME tag identifies the documentation publisher.
+# The default value is: Publisher.
+# This tag requires that the tag GENERATE_DOCSET is set to YES.
+
+DOCSET_PUBLISHER_NAME  = Publisher
+
+# If the GENERATE_HTMLHELP tag is set to YES then doxygen generates three
+# additional HTML index files: index.hhp, index.hhc, and index.hhk. The
+# index.hhp is a project file that can be read by Microsoft's HTML Help Workshop
+# (see: http://www.microsoft.com/en-us/download/details.aspx?id=21138) on
+# Windows.
+#
+# The HTML Help Workshop contains a compiler that can convert all HTML output
+# generated by doxygen into a single compiled HTML file (.chm). Compiled HTML
+# files are now used as the Windows 98 help format, and will replace the old
+# Windows help format (.hlp) on all Windows platforms in the future. Compressed
+# HTML files also contain an index, a table of contents, and you can search for
+# words in the documentation. The HTML workshop also contains a viewer for
+# compressed HTML files.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+GENERATE_HTMLHELP      = NO
+
+# The CHM_FILE tag can be used to specify the file name of the resulting .chm
+# file. You can add a path in front of the file if the result should not be
+# written to the html output directory.
+# This tag requires that the tag GENERATE_HTMLHELP is set to YES.
+
+CHM_FILE               =
+
+# The HHC_LOCATION tag can be used to specify the location (absolute path
+# including file name) of the HTML help compiler ( hhc.exe). If non-empty
+# doxygen will try to run the HTML help compiler on the generated index.hhp.
+# The file has to be specified with full path.
+# This tag requires that the tag GENERATE_HTMLHELP is set to YES.
+
+HHC_LOCATION           =
+
+# The GENERATE_CHI flag controls if a separate .chi index file is generated (
+# YES) or that it should be included in the master .chm file ( NO).
+# The default value is: NO.
+# This tag requires that the tag GENERATE_HTMLHELP is set to YES.
+
+GENERATE_CHI           = NO
+
+# The CHM_INDEX_ENCODING is used to encode HtmlHelp index ( hhk), content ( hhc)
+# and project file content.
+# This tag requires that the tag GENERATE_HTMLHELP is set to YES.
+
+CHM_INDEX_ENCODING     =
+
+# The BINARY_TOC flag controls whether a binary table of contents is generated (
+# YES) or a normal table of contents ( NO) in the .chm file. Furthermore it
+# enables the Previous and Next buttons.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_HTMLHELP is set to YES.
+
+BINARY_TOC             = NO
+
+# The TOC_EXPAND flag can be set to YES to add extra items for group members to
+# the table of contents of the HTML help documentation and to the tree view.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_HTMLHELP is set to YES.
+
+TOC_EXPAND             = NO
+
+# If the GENERATE_QHP tag is set to YES and both QHP_NAMESPACE and
+# QHP_VIRTUAL_FOLDER are set, an additional index file will be generated that
+# can be used as input for Qt's qhelpgenerator to generate a Qt Compressed Help
+# (.qch) of the generated HTML documentation.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+GENERATE_QHP           = NO
+
+# If the QHG_LOCATION tag is specified, the QCH_FILE tag can be used to specify
+# the file name of the resulting .qch file. The path specified is relative to
+# the HTML output folder.
+# This tag requires that the tag GENERATE_QHP is set to YES.
+
+QCH_FILE               =
+
+# The QHP_NAMESPACE tag specifies the namespace to use when generating Qt Help
+# Project output. For more information please see Qt Help Project / Namespace
+# (see: http://qt-project.org/doc/qt-4.8/qthelpproject.html#namespace).
+# The default value is: org.doxygen.Project.
+# This tag requires that the tag GENERATE_QHP is set to YES.
+
+QHP_NAMESPACE          = org.doxygen.Project
+
+# The QHP_VIRTUAL_FOLDER tag specifies the namespace to use when generating Qt
+# Help Project output. For more information please see Qt Help Project / Virtual
+# Folders (see: http://qt-project.org/doc/qt-4.8/qthelpproject.html#virtual-
+# folders).
+# The default value is: doc.
+# This tag requires that the tag GENERATE_QHP is set to YES.
+
+QHP_VIRTUAL_FOLDER     = doc
+
+# If the QHP_CUST_FILTER_NAME tag is set, it specifies the name of a custom
+# filter to add. For more information please see Qt Help Project / Custom
+# Filters (see: http://qt-project.org/doc/qt-4.8/qthelpproject.html#custom-
+# filters).
+# This tag requires that the tag GENERATE_QHP is set to YES.
+
+QHP_CUST_FILTER_NAME   =
+
+# The QHP_CUST_FILTER_ATTRS tag specifies the list of the attributes of the
+# custom filter to add. For more information please see Qt Help Project / Custom
+# Filters (see: http://qt-project.org/doc/qt-4.8/qthelpproject.html#custom-
+# filters).
+# This tag requires that the tag GENERATE_QHP is set to YES.
+
+QHP_CUST_FILTER_ATTRS  =
+
+# The QHP_SECT_FILTER_ATTRS tag specifies the list of the attributes this
+# project's filter section matches. Qt Help Project / Filter Attributes (see:
+# http://qt-project.org/doc/qt-4.8/qthelpproject.html#filter-attributes).
+# This tag requires that the tag GENERATE_QHP is set to YES.
+
+QHP_SECT_FILTER_ATTRS  =
+
+# The QHG_LOCATION tag can be used to specify the location of Qt's
+# qhelpgenerator. If non-empty doxygen will try to run qhelpgenerator on the
+# generated .qhp file.
+# This tag requires that the tag GENERATE_QHP is set to YES.
+
+QHG_LOCATION           =
+
+# If the GENERATE_ECLIPSEHELP tag is set to YES, additional index files will be
+# generated, together with the HTML files, they form an Eclipse help plugin. To
+# install this plugin and make it available under the help contents menu in
+# Eclipse, the contents of the directory containing the HTML and XML files needs
+# to be copied into the plugins directory of eclipse. The name of the directory
+# within the plugins directory should be the same as the ECLIPSE_DOC_ID value.
+# After copying Eclipse needs to be restarted before the help appears.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+GENERATE_ECLIPSEHELP   = NO
+
+# A unique identifier for the Eclipse help plugin. When installing the plugin
+# the directory name containing the HTML and XML files should also have this
+# name. Each documentation set should have its own identifier.
+# The default value is: org.doxygen.Project.
+# This tag requires that the tag GENERATE_ECLIPSEHELP is set to YES.
+
+ECLIPSE_DOC_ID         = org.doxygen.Project
+
+# If you want full control over the layout of the generated HTML pages it might
+# be necessary to disable the index and replace it with your own. The
+# DISABLE_INDEX tag can be used to turn on/off the condensed index (tabs) at top
+# of each HTML page. A value of NO enables the index and the value YES disables
+# it. Since the tabs in the index contain the same information as the navigation
+# tree, you can set this option to YES if you also set GENERATE_TREEVIEW to YES.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+DISABLE_INDEX          = NO
+
+# The GENERATE_TREEVIEW tag is used to specify whether a tree-like index
+# structure should be generated to display hierarchical information. If the tag
+# value is set to YES, a side panel will be generated containing a tree-like
+# index structure (just like the one that is generated for HTML Help). For this
+# to work a browser that supports JavaScript, DHTML, CSS and frames is required
+# (i.e. any modern browser). Windows users are probably better off using the
+# HTML help feature. Via custom stylesheets (see HTML_EXTRA_STYLESHEET) one can
+# further fine-tune the look of the index. As an example, the default style
+# sheet generated by doxygen has an example that shows how to put an image at
+# the root of the tree instead of the PROJECT_NAME. Since the tree basically has
+# the same information as the tab index, you could consider setting
+# DISABLE_INDEX to YES when enabling this option.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+GENERATE_TREEVIEW      = NO
+
+# The ENUM_VALUES_PER_LINE tag can be used to set the number of enum values that
+# doxygen will group on one line in the generated HTML documentation.
+#
+# Note that a value of 0 will completely suppress the enum values from appearing
+# in the overview section.
+# Minimum value: 0, maximum value: 20, default value: 4.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+ENUM_VALUES_PER_LINE   = 4
+
+# If the treeview is enabled (see GENERATE_TREEVIEW) then this tag can be used
+# to set the initial width (in pixels) of the frame in which the tree is shown.
+# Minimum value: 0, maximum value: 1500, default value: 250.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+TREEVIEW_WIDTH         = 250
+
+# When the EXT_LINKS_IN_WINDOW option is set to YES doxygen will open links to
+# external symbols imported via tag files in a separate window.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+EXT_LINKS_IN_WINDOW    = NO
+
+# Use this tag to change the font size of LaTeX formulas included as images in
+# the HTML documentation. When you change the font size after a successful
+# doxygen run you need to manually remove any form_*.png images from the HTML
+# output directory to force them to be regenerated.
+# Minimum value: 8, maximum value: 50, default value: 10.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+FORMULA_FONTSIZE       = 10
+
+# Use the FORMULA_TRANPARENT tag to determine whether or not the images
+# generated for formulas are transparent PNGs. Transparent PNGs are not
+# supported properly for IE 6.0, but are supported on all modern browsers.
+#
+# Note that when changing this option you need to delete any form_*.png files in
+# the HTML output directory before the changes have effect.
+# The default value is: YES.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+FORMULA_TRANSPARENT    = YES
+
+# Enable the USE_MATHJAX option to render LaTeX formulas using MathJax (see
+# http://www.mathjax.org) which uses client side Javascript for the rendering
+# instead of using prerendered bitmaps. Use this if you do not have LaTeX
+# installed or if you want to formulas look prettier in the HTML output. When
+# enabled you may also need to install MathJax separately and configure the path
+# to it using the MATHJAX_RELPATH option.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+USE_MATHJAX            = NO
+
+# When MathJax is enabled you can set the default output format to be used for
+# the MathJax output. See the MathJax site (see:
+# http://docs.mathjax.org/en/latest/output.html) for more details.
+# Possible values are: HTML-CSS (which is slower, but has the best
+# compatibility), NativeMML (i.e. MathML) and SVG.
+# The default value is: HTML-CSS.
+# This tag requires that the tag USE_MATHJAX is set to YES.
+
+MATHJAX_FORMAT         = HTML-CSS
+
+# When MathJax is enabled you need to specify the location relative to the HTML
+# output directory using the MATHJAX_RELPATH option. The destination directory
+# should contain the MathJax.js script. For instance, if the mathjax directory
+# is located at the same level as the HTML output directory, then
+# MATHJAX_RELPATH should be ../mathjax. The default value points to the MathJax
+# Content Delivery Network so you can quickly see the result without installing
+# MathJax. However, it is strongly recommended to install a local copy of
+# MathJax from http://www.mathjax.org before deployment.
+# The default value is: http://cdn.mathjax.org/mathjax/latest.
+# This tag requires that the tag USE_MATHJAX is set to YES.
+
+MATHJAX_RELPATH        = http://cdn.mathjax.org/mathjax/latest
+
+# The MATHJAX_EXTENSIONS tag can be used to specify one or more MathJax
+# extension names that should be enabled during MathJax rendering. For example
+# MATHJAX_EXTENSIONS = TeX/AMSmath TeX/AMSsymbols
+# This tag requires that the tag USE_MATHJAX is set to YES.
+
+MATHJAX_EXTENSIONS     =
+
+# The MATHJAX_CODEFILE tag can be used to specify a file with javascript pieces
+# of code that will be used on startup of the MathJax code. See the MathJax site
+# (see: http://docs.mathjax.org/en/latest/output.html) for more details. For an
+# example see the documentation.
+# This tag requires that the tag USE_MATHJAX is set to YES.
+
+MATHJAX_CODEFILE       =
+
+# When the SEARCHENGINE tag is enabled doxygen will generate a search box for
+# the HTML output. The underlying search engine uses javascript and DHTML and
+# should work on any modern browser. Note that when using HTML help
+# (GENERATE_HTMLHELP), Qt help (GENERATE_QHP), or docsets (GENERATE_DOCSET)
+# there is already a search function so this one should typically be disabled.
+# For large projects the javascript based search engine can be slow, then
+# enabling SERVER_BASED_SEARCH may provide a better solution. It is possible to
+# search using the keyboard; to jump to the search box use <access key> + S
+# (what the <access key> is depends on the OS and browser, but it is typically
+# <CTRL>, <ALT>/<option>, or both). Inside the search box use the <cursor down
+# key> to jump into the search results window, the results can be navigated
+# using the <cursor keys>. Press <Enter> to select an item or <escape> to cancel
+# the search. The filter options can be selected when the cursor is inside the
+# search box by pressing <Shift>+<cursor down>. Also here use the <cursor keys>
+# to select a filter and <Enter> or <escape> to activate or cancel the filter
+# option.
+# The default value is: YES.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+SEARCHENGINE           = YES
+
+# When the SERVER_BASED_SEARCH tag is enabled the search engine will be
+# implemented using a web server instead of a web client using Javascript. There
+# are two flavors of web server based searching depending on the EXTERNAL_SEARCH
+# setting. When disabled, doxygen will generate a PHP script for searching and
+# an index file used by the script. When EXTERNAL_SEARCH is enabled the indexing
+# and searching needs to be provided by external tools. See the section
+# "External Indexing and Searching" for details.
+# The default value is: NO.
+# This tag requires that the tag SEARCHENGINE is set to YES.
+
+SERVER_BASED_SEARCH    = NO
+
+# When EXTERNAL_SEARCH tag is enabled doxygen will no longer generate the PHP
+# script for searching. Instead the search results are written to an XML file
+# which needs to be processed by an external indexer. Doxygen will invoke an
+# external search engine pointed to by the SEARCHENGINE_URL option to obtain the
+# search results.
+#
+# Doxygen ships with an example indexer ( doxyindexer) and search engine
+# (doxysearch.cgi) which are based on the open source search engine library
+# Xapian (see: http://xapian.org/).
+#
+# See the section "External Indexing and Searching" for details.
+# The default value is: NO.
+# This tag requires that the tag SEARCHENGINE is set to YES.
+
+EXTERNAL_SEARCH        = NO
+
+# The SEARCHENGINE_URL should point to a search engine hosted by a web server
+# which will return the search results when EXTERNAL_SEARCH is enabled.
+#
+# Doxygen ships with an example indexer ( doxyindexer) and search engine
+# (doxysearch.cgi) which are based on the open source search engine library
+# Xapian (see: http://xapian.org/). See the section "External Indexing and
+# Searching" for details.
+# This tag requires that the tag SEARCHENGINE is set to YES.
+
+SEARCHENGINE_URL       =
+
+# When SERVER_BASED_SEARCH and EXTERNAL_SEARCH are both enabled the unindexed
+# search data is written to a file for indexing by an external tool. With the
+# SEARCHDATA_FILE tag the name of this file can be specified.
+# The default file is: searchdata.xml.
+# This tag requires that the tag SEARCHENGINE is set to YES.
+
+SEARCHDATA_FILE        = searchdata.xml
+
+# When SERVER_BASED_SEARCH and EXTERNAL_SEARCH are both enabled the
+# EXTERNAL_SEARCH_ID tag can be used as an identifier for the project. This is
+# useful in combination with EXTRA_SEARCH_MAPPINGS to search through multiple
+# projects and redirect the results back to the right project.
+# This tag requires that the tag SEARCHENGINE is set to YES.
+
+EXTERNAL_SEARCH_ID     =
+
+# The EXTRA_SEARCH_MAPPINGS tag can be used to enable searching through doxygen
+# projects other than the one defined by this configuration file, but that are
+# all added to the same external search index. Each project needs to have a
+# unique id set via EXTERNAL_SEARCH_ID. The search mapping then maps the id of
+# to a relative location where the documentation can be found. The format is:
+# EXTRA_SEARCH_MAPPINGS = tagname1=loc1 tagname2=loc2 ...
+# This tag requires that the tag SEARCHENGINE is set to YES.
+
+EXTRA_SEARCH_MAPPINGS  =
+
+#---------------------------------------------------------------------------
+# Configuration options related to the LaTeX output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_LATEX tag is set to YES doxygen will generate LaTeX output.
+# The default value is: YES.
+
+GENERATE_LATEX         = NO
+
+# The LATEX_OUTPUT tag is used to specify where the LaTeX docs will be put. If a
+# relative path is entered the value of OUTPUT_DIRECTORY will be put in front of
+# it.
+# The default directory is: latex.
+# This tag requires that the tag GENERATE_LATEX is set to YES.
+
+LATEX_OUTPUT           = latex
+
+# The LATEX_CMD_NAME tag can be used to specify the LaTeX command name to be
+# invoked.
+#
+# Note that when enabling USE_PDFLATEX this option is only used for generating
+# bitmaps for formulas in the HTML output, but not in the Makefile that is
+# written to the output directory.
+# The default file is: latex.
+# This tag requires that the tag GENERATE_LATEX is set to YES.
+
+LATEX_CMD_NAME         = latex
+
+# The MAKEINDEX_CMD_NAME tag can be used to specify the command name to generate
+# index for LaTeX.
+# The default file is: makeindex.
+# This tag requires that the tag GENERATE_LATEX is set to YES.
+
+MAKEINDEX_CMD_NAME     = makeindex
+
+# If the COMPACT_LATEX tag is set to YES doxygen generates more compact LaTeX
+# documents. This may be useful for small projects and may help to save some
+# trees in general.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_LATEX is set to YES.
+
+COMPACT_LATEX          = NO
+
+# The PAPER_TYPE tag can be used to set the paper type that is used by the
+# printer.
+# Possible values are: a4 (210 x 297 mm), letter (8.5 x 11 inches), legal (8.5 x
+# 14 inches) and executive (7.25 x 10.5 inches).
+# The default value is: a4.
+# This tag requires that the tag GENERATE_LATEX is set to YES.
+
+PAPER_TYPE             = a4
+
+# The EXTRA_PACKAGES tag can be used to specify one or more LaTeX package names
+# that should be included in the LaTeX output. To get the times font for
+# instance you can specify
+# EXTRA_PACKAGES=times
+# If left blank no extra packages will be included.
+# This tag requires that the tag GENERATE_LATEX is set to YES.
+
+EXTRA_PACKAGES         =
+
+# The LATEX_HEADER tag can be used to specify a personal LaTeX header for the
+# generated LaTeX document. The header should contain everything until the first
+# chapter. If it is left blank doxygen will generate a standard header. See
+# section "Doxygen usage" for information on how to let doxygen write the
+# default header to a separate file.
+#
+# Note: Only use a user-defined header if you know what you are doing! The
+# following commands have a special meaning inside the header: $title,
+# $datetime, $date, $doxygenversion, $projectname, $projectnumber,
+# $projectbrief, $projectlogo. Doxygen will replace $title with the empy string,
+# for the replacement values of the other commands the user is refered to
+# HTML_HEADER.
+# This tag requires that the tag GENERATE_LATEX is set to YES.
+
+LATEX_HEADER           =
+
+# The LATEX_FOOTER tag can be used to specify a personal LaTeX footer for the
+# generated LaTeX document. The footer should contain everything after the last
+# chapter. If it is left blank doxygen will generate a standard footer. See
+# LATEX_HEADER for more information on how to generate a default footer and what
+# special commands can be used inside the footer.
+#
+# Note: Only use a user-defined footer if you know what you are doing!
+# This tag requires that the tag GENERATE_LATEX is set to YES.
+
+LATEX_FOOTER           =
+
+# The LATEX_EXTRA_FILES tag can be used to specify one or more extra images or
+# other source files which should be copied to the LATEX_OUTPUT output
+# directory. Note that the files will be copied as-is; there are no commands or
+# markers available.
+# This tag requires that the tag GENERATE_LATEX is set to YES.
+
+LATEX_EXTRA_FILES      =
+
+# If the PDF_HYPERLINKS tag is set to YES, the LaTeX that is generated is
+# prepared for conversion to PDF (using ps2pdf or pdflatex). The PDF file will
+# contain links (just like the HTML output) instead of page references. This
+# makes the output suitable for online browsing using a PDF viewer.
+# The default value is: YES.
+# This tag requires that the tag GENERATE_LATEX is set to YES.
+
+PDF_HYPERLINKS         = YES
+
+# If the USE_PDFLATEX tag is set to YES, doxygen will use pdflatex to generate
+# the PDF file directly from the LaTeX files. Set this option to YES to get a
+# higher quality PDF documentation.
+# The default value is: YES.
+# This tag requires that the tag GENERATE_LATEX is set to YES.
+
+USE_PDFLATEX           = YES
+
+# If the LATEX_BATCHMODE tag is set to YES, doxygen will add the \batchmode
+# command to the generated LaTeX files. This will instruct LaTeX to keep running
+# if errors occur, instead of asking the user for help. This option is also used
+# when generating formulas in HTML.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_LATEX is set to YES.
+
+LATEX_BATCHMODE        = NO
+
+# If the LATEX_HIDE_INDICES tag is set to YES then doxygen will not include the
+# index chapters (such as File Index, Compound Index, etc.) in the output.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_LATEX is set to YES.
+
+LATEX_HIDE_INDICES     = NO
+
+# If the LATEX_SOURCE_CODE tag is set to YES then doxygen will include source
+# code with syntax highlighting in the LaTeX output.
+#
+# Note that which sources are shown also depends on other settings such as
+# SOURCE_BROWSER.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_LATEX is set to YES.
+
+LATEX_SOURCE_CODE      = NO
+
+# The LATEX_BIB_STYLE tag can be used to specify the style to use for the
+# bibliography, e.g. plainnat, or ieeetr. See
+# http://en.wikipedia.org/wiki/BibTeX and \cite for more info.
+# The default value is: plain.
+# This tag requires that the tag GENERATE_LATEX is set to YES.
+
+LATEX_BIB_STYLE        = plain
+
+#---------------------------------------------------------------------------
+# Configuration options related to the RTF output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_RTF tag is set to YES doxygen will generate RTF output. The
+# RTF output is optimized for Word 97 and may not look too pretty with other RTF
+# readers/editors.
+# The default value is: NO.
+
+GENERATE_RTF           = NO
+
+# The RTF_OUTPUT tag is used to specify where the RTF docs will be put. If a
+# relative path is entered the value of OUTPUT_DIRECTORY will be put in front of
+# it.
+# The default directory is: rtf.
+# This tag requires that the tag GENERATE_RTF is set to YES.
+
+RTF_OUTPUT             = rtf
+
+# If the COMPACT_RTF tag is set to YES doxygen generates more compact RTF
+# documents. This may be useful for small projects and may help to save some
+# trees in general.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_RTF is set to YES.
+
+COMPACT_RTF            = NO
+
+# If the RTF_HYPERLINKS tag is set to YES, the RTF that is generated will
+# contain hyperlink fields. The RTF file will contain links (just like the HTML
+# output) instead of page references. This makes the output suitable for online
+# browsing using Word or some other Word compatible readers that support those
+# fields.
+#
+# Note: WordPad (write) and others do not support links.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_RTF is set to YES.
+
+RTF_HYPERLINKS         = NO
+
+# Load stylesheet definitions from file. Syntax is similar to doxygen's config
+# file, i.e. a series of assignments. You only have to provide replacements,
+# missing definitions are set to their default value.
+#
+# See also section "Doxygen usage" for information on how to generate the
+# default style sheet that doxygen normally uses.
+# This tag requires that the tag GENERATE_RTF is set to YES.
+
+RTF_STYLESHEET_FILE    =
+
+# Set optional variables used in the generation of an RTF document. Syntax is
+# similar to doxygen's config file. A template extensions file can be generated
+# using doxygen -e rtf extensionFile.
+# This tag requires that the tag GENERATE_RTF is set to YES.
+
+RTF_EXTENSIONS_FILE    =
+
+#---------------------------------------------------------------------------
+# Configuration options related to the man page output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_MAN tag is set to YES doxygen will generate man pages for
+# classes and files.
+# The default value is: NO.
+
+GENERATE_MAN           = NO
+
+# The MAN_OUTPUT tag is used to specify where the man pages will be put. If a
+# relative path is entered the value of OUTPUT_DIRECTORY will be put in front of
+# it. A directory man3 will be created inside the directory specified by
+# MAN_OUTPUT.
+# The default directory is: man.
+# This tag requires that the tag GENERATE_MAN is set to YES.
+
+MAN_OUTPUT             = man
+
+# The MAN_EXTENSION tag determines the extension that is added to the generated
+# man pages. In case the manual section does not start with a number, the number
+# 3 is prepended. The dot (.) at the beginning of the MAN_EXTENSION tag is
+# optional.
+# The default value is: .3.
+# This tag requires that the tag GENERATE_MAN is set to YES.
+
+MAN_EXTENSION          = .3
+
+# The MAN_SUBDIR tag determines the name of the directory created within
+# MAN_OUTPUT in which the man pages are placed. If defaults to man followed by
+# MAN_EXTENSION with the initial . removed.
+# This tag requires that the tag GENERATE_MAN is set to YES.
+
+MAN_SUBDIR             =
+
+# If the MAN_LINKS tag is set to YES and doxygen generates man output, then it
+# will generate one additional man file for each entity documented in the real
+# man page(s). These additional files only source the real man page, but without
+# them the man command would be unable to find the correct page.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_MAN is set to YES.
+
+MAN_LINKS              = NO
+
+#---------------------------------------------------------------------------
+# Configuration options related to the XML output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_XML tag is set to YES doxygen will generate an XML file that
+# captures the structure of the code including all documentation.
+# The default value is: NO.
+
+GENERATE_XML           = NO
+
+# The XML_OUTPUT tag is used to specify where the XML pages will be put. If a
+# relative path is entered the value of OUTPUT_DIRECTORY will be put in front of
+# it.
+# The default directory is: xml.
+# This tag requires that the tag GENERATE_XML is set to YES.
+
+XML_OUTPUT             = xml
+
+# If the XML_PROGRAMLISTING tag is set to YES doxygen will dump the program
+# listings (including syntax highlighting and cross-referencing information) to
+# the XML output. Note that enabling this will significantly increase the size
+# of the XML output.
+# The default value is: YES.
+# This tag requires that the tag GENERATE_XML is set to YES.
+
+XML_PROGRAMLISTING     = YES
+
+#---------------------------------------------------------------------------
+# Configuration options related to the DOCBOOK output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_DOCBOOK tag is set to YES doxygen will generate Docbook files
+# that can be used to generate PDF.
+# The default value is: NO.
+
+GENERATE_DOCBOOK       = NO
+
+# The DOCBOOK_OUTPUT tag is used to specify where the Docbook pages will be put.
+# If a relative path is entered the value of OUTPUT_DIRECTORY will be put in
+# front of it.
+# The default directory is: docbook.
+# This tag requires that the tag GENERATE_DOCBOOK is set to YES.
+
+DOCBOOK_OUTPUT         = docbook
+
+# If the DOCBOOK_PROGRAMLISTING tag is set to YES doxygen will include the
+# program listings (including syntax highlighting and cross-referencing
+# information) to the DOCBOOK output. Note that enabling this will significantly
+# increase the size of the DOCBOOK output.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_DOCBOOK is set to YES.
+
+DOCBOOK_PROGRAMLISTING = NO
+
+#---------------------------------------------------------------------------
+# Configuration options for the AutoGen Definitions output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_AUTOGEN_DEF tag is set to YES doxygen will generate an AutoGen
+# Definitions (see http://autogen.sf.net) file that captures the structure of
+# the code including all documentation. Note that this feature is still
+# experimental and incomplete at the moment.
+# The default value is: NO.
+
+GENERATE_AUTOGEN_DEF   = NO
+
+#---------------------------------------------------------------------------
+# Configuration options related to the Perl module output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_PERLMOD tag is set to YES doxygen will generate a Perl module
+# file that captures the structure of the code including all documentation.
+#
+# Note that this feature is still experimental and incomplete at the moment.
+# The default value is: NO.
+
+GENERATE_PERLMOD       = NO
+
+# If the PERLMOD_LATEX tag is set to YES doxygen will generate the necessary
+# Makefile rules, Perl scripts and LaTeX code to be able to generate PDF and DVI
+# output from the Perl module output.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_PERLMOD is set to YES.
+
+PERLMOD_LATEX          = NO
+
+# If the PERLMOD_PRETTY tag is set to YES the Perl module output will be nicely
+# formatted so it can be parsed by a human reader. This is useful if you want to
+# understand what is going on. On the other hand, if this tag is set to NO the
+# size of the Perl module output will be much smaller and Perl will parse it
+# just the same.
+# The default value is: YES.
+# This tag requires that the tag GENERATE_PERLMOD is set to YES.
+
+PERLMOD_PRETTY         = YES
+
+# The names of the make variables in the generated doxyrules.make file are
+# prefixed with the string contained in PERLMOD_MAKEVAR_PREFIX. This is useful
+# so different doxyrules.make files included by the same Makefile don't
+# overwrite each other's variables.
+# This tag requires that the tag GENERATE_PERLMOD is set to YES.
+
+PERLMOD_MAKEVAR_PREFIX =
+
+#---------------------------------------------------------------------------
+# Configuration options related to the preprocessor
+#---------------------------------------------------------------------------
+
+# If the ENABLE_PREPROCESSING tag is set to YES doxygen will evaluate all
+# C-preprocessor directives found in the sources and include files.
+# The default value is: YES.
+
+ENABLE_PREPROCESSING   = YES
+
+# If the MACRO_EXPANSION tag is set to YES doxygen will expand all macro names
+# in the source code. If set to NO only conditional compilation will be
+# performed. Macro expansion can be done in a controlled way by setting
+# EXPAND_ONLY_PREDEF to YES.
+# The default value is: NO.
+# This tag requires that the tag ENABLE_PREPROCESSING is set to YES.
+
+MACRO_EXPANSION        = NO
+
+# If the EXPAND_ONLY_PREDEF and MACRO_EXPANSION tags are both set to YES then
+# the macro expansion is limited to the macros specified with the PREDEFINED and
+# EXPAND_AS_DEFINED tags.
+# The default value is: NO.
+# This tag requires that the tag ENABLE_PREPROCESSING is set to YES.
+
+EXPAND_ONLY_PREDEF     = NO
+
+# If the SEARCH_INCLUDES tag is set to YES the includes files in the
+# INCLUDE_PATH will be searched if a #include is found.
+# The default value is: YES.
+# This tag requires that the tag ENABLE_PREPROCESSING is set to YES.
+
+SEARCH_INCLUDES        = YES
+
+# The INCLUDE_PATH tag can be used to specify one or more directories that
+# contain include files that are not input files but should be processed by the
+# preprocessor.
+# This tag requires that the tag SEARCH_INCLUDES is set to YES.
+
+INCLUDE_PATH           =
+
+# You can use the INCLUDE_FILE_PATTERNS tag to specify one or more wildcard
+# patterns (like *.h and *.hpp) to filter out the header-files in the
+# directories. If left blank, the patterns specified with FILE_PATTERNS will be
+# used.
+# This tag requires that the tag ENABLE_PREPROCESSING is set to YES.
+
+INCLUDE_FILE_PATTERNS  =
+
+# The PREDEFINED tag can be used to specify one or more macro names that are
+# defined before the preprocessor is started (similar to the -D option of e.g.
+# gcc). The argument of the tag is a list of macros of the form: name or
+# name=definition (no spaces). If the definition and the "=" are omitted, "=1"
+# is assumed. To prevent a macro definition from being undefined via #undef or
+# recursively expanded use the := operator instead of the = operator.
+# This tag requires that the tag ENABLE_PREPROCESSING is set to YES.
+
+PREDEFINED             =
+
+# If the MACRO_EXPANSION and EXPAND_ONLY_PREDEF tags are set to YES then this
+# tag can be used to specify a list of macro names that should be expanded. The
+# macro definition that is found in the sources will be used. Use the PREDEFINED
+# tag if you want to use a different macro definition that overrules the
+# definition found in the source code.
+# This tag requires that the tag ENABLE_PREPROCESSING is set to YES.
+
+EXPAND_AS_DEFINED      =
+
+# If the SKIP_FUNCTION_MACROS tag is set to YES then doxygen's preprocessor will
+# remove all references to function-like macros that are alone on a line, have
+# an all uppercase name, and do not end with a semicolon. Such function macros
+# are typically used for boiler-plate code, and will confuse the parser if not
+# removed.
+# The default value is: YES.
+# This tag requires that the tag ENABLE_PREPROCESSING is set to YES.
+
+SKIP_FUNCTION_MACROS   = YES
+
+#---------------------------------------------------------------------------
+# Configuration options related to external references
+#---------------------------------------------------------------------------
+
+# The TAGFILES tag can be used to specify one or more tag files. For each tag
+# file the location of the external documentation should be added. The format of
+# a tag file without this location is as follows:
+# TAGFILES = file1 file2 ...
+# Adding location for the tag files is done as follows:
+# TAGFILES = file1=loc1 "file2 = loc2" ...
+# where loc1 and loc2 can be relative or absolute paths or URLs. See the
+# section "Linking to external documentation" for more information about the use
+# of tag files.
+# Note: Each tag file must have a unique name (where the name does NOT include
+# the path). If a tag file is not located in the directory in which doxygen is
+# run, you must also specify the path to the tagfile here.
+
+TAGFILES               =
+
+# When a file name is specified after GENERATE_TAGFILE, doxygen will create a
+# tag file that is based on the input files it reads. See section "Linking to
+# external documentation" for more information about the usage of tag files.
+
+GENERATE_TAGFILE       =
+
+# If the ALLEXTERNALS tag is set to YES all external class will be listed in the
+# class index. If set to NO only the inherited external classes will be listed.
+# The default value is: NO.
+
+ALLEXTERNALS           = NO
+
+# If the EXTERNAL_GROUPS tag is set to YES all external groups will be listed in
+# the modules index. If set to NO, only the current project's groups will be
+# listed.
+# The default value is: YES.
+
+EXTERNAL_GROUPS        = YES
+
+# If the EXTERNAL_PAGES tag is set to YES all external pages will be listed in
+# the related pages index. If set to NO, only the current project's pages will
+# be listed.
+# The default value is: YES.
+
+EXTERNAL_PAGES         = YES
+
+# The PERL_PATH should be the absolute path and name of the perl script
+# interpreter (i.e. the result of 'which perl').
+# The default file (with absolute path) is: /usr/bin/perl.
+
+PERL_PATH              = /usr/bin/perl
+
+#---------------------------------------------------------------------------
+# Configuration options related to the dot tool
+#---------------------------------------------------------------------------
+
+# If the CLASS_DIAGRAMS tag is set to YES doxygen will generate a class diagram
+# (in HTML and LaTeX) for classes with base or super classes. Setting the tag to
+# NO turns the diagrams off. Note that this option also works with HAVE_DOT
+# disabled, but it is recommended to install and use dot, since it yields more
+# powerful graphs.
+# The default value is: YES.
+
+CLASS_DIAGRAMS         = YES
+
+# You can define message sequence charts within doxygen comments using the \msc
+# command. Doxygen will then run the mscgen tool (see:
+# http://www.mcternan.me.uk/mscgen/)) to produce the chart and insert it in the
+# documentation. The MSCGEN_PATH tag allows you to specify the directory where
+# the mscgen tool resides. If left empty the tool is assumed to be found in the
+# default search path.
+
+MSCGEN_PATH            =
+
+# You can include diagrams made with dia in doxygen documentation. Doxygen will
+# then run dia to produce the diagram and insert it in the documentation. The
+# DIA_PATH tag allows you to specify the directory where the dia binary resides.
+# If left empty dia is assumed to be found in the default search path.
+
+DIA_PATH               =
+
+# If set to YES, the inheritance and collaboration graphs will hide inheritance
+# and usage relations if the target is undocumented or is not a class.
+# The default value is: YES.
+
+HIDE_UNDOC_RELATIONS   = YES
+
+# If you set the HAVE_DOT tag to YES then doxygen will assume the dot tool is
+# available from the path. This tool is part of Graphviz (see:
+# http://www.graphviz.org/), a graph visualization toolkit from AT&T and Lucent
+# Bell Labs. The other options in this section have no effect if this option is
+# set to NO
+# The default value is: YES.
+
+HAVE_DOT               = YES
+
+# The DOT_NUM_THREADS specifies the number of dot invocations doxygen is allowed
+# to run in parallel. When set to 0 doxygen will base this on the number of
+# processors available in the system. You can set it explicitly to a value
+# larger than 0 to get control over the balance between CPU load and processing
+# speed.
+# Minimum value: 0, maximum value: 32, default value: 0.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+DOT_NUM_THREADS        = 0
+
+# When you want a differently looking font in the dot files that doxygen
+# generates you can specify the font name using DOT_FONTNAME. You need to make
+# sure dot is able to find the font, which can be done by putting it in a
+# standard location or by setting the DOTFONTPATH environment variable or by
+# setting DOT_FONTPATH to the directory containing the font.
+# The default value is: Helvetica.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+DOT_FONTNAME           = Helvetica
+
+# The DOT_FONTSIZE tag can be used to set the size (in points) of the font of
+# dot graphs.
+# Minimum value: 4, maximum value: 24, default value: 10.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+DOT_FONTSIZE           = 10
+
+# By default doxygen will tell dot to use the default font as specified with
+# DOT_FONTNAME. If you specify a different font using DOT_FONTNAME you can set
+# the path where dot can find it using this tag.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+DOT_FONTPATH           =
+
+# If the CLASS_GRAPH tag is set to YES then doxygen will generate a graph for
+# each documented class showing the direct and indirect inheritance relations.
+# Setting this tag to YES will force the CLASS_DIAGRAMS tag to NO.
+# The default value is: YES.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+CLASS_GRAPH            = YES
+
+# If the COLLABORATION_GRAPH tag is set to YES then doxygen will generate a
+# graph for each documented class showing the direct and indirect implementation
+# dependencies (inheritance, containment, and class references variables) of the
+# class with other documented classes.
+# The default value is: YES.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+COLLABORATION_GRAPH    = YES
+
+# If the GROUP_GRAPHS tag is set to YES then doxygen will generate a graph for
+# groups, showing the direct groups dependencies.
+# The default value is: YES.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+GROUP_GRAPHS           = YES
+
+# If the UML_LOOK tag is set to YES doxygen will generate inheritance and
+# collaboration diagrams in a style similar to the OMG's Unified Modeling
+# Language.
+# The default value is: NO.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+UML_LOOK               = NO
+
+# If the UML_LOOK tag is enabled, the fields and methods are shown inside the
+# class node. If there are many fields or methods and many nodes the graph may
+# become too big to be useful. The UML_LIMIT_NUM_FIELDS threshold limits the
+# number of items for each type to make the size more manageable. Set this to 0
+# for no limit. Note that the threshold may be exceeded by 50% before the limit
+# is enforced. So when you set the threshold to 10, up to 15 fields may appear,
+# but if the number exceeds 15, the total amount of fields shown is limited to
+# 10.
+# Minimum value: 0, maximum value: 100, default value: 10.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+UML_LIMIT_NUM_FIELDS   = 10
+
+# If the TEMPLATE_RELATIONS tag is set to YES then the inheritance and
+# collaboration graphs will show the relations between templates and their
+# instances.
+# The default value is: NO.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+TEMPLATE_RELATIONS     = NO
+
+# If the INCLUDE_GRAPH, ENABLE_PREPROCESSING and SEARCH_INCLUDES tags are set to
+# YES then doxygen will generate a graph for each documented file showing the
+# direct and indirect include dependencies of the file with other documented
+# files.
+# The default value is: YES.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+INCLUDE_GRAPH          = YES
+
+# If the INCLUDED_BY_GRAPH, ENABLE_PREPROCESSING and SEARCH_INCLUDES tags are
+# set to YES then doxygen will generate a graph for each documented file showing
+# the direct and indirect include dependencies of the file with other documented
+# files.
+# The default value is: YES.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+INCLUDED_BY_GRAPH      = YES
+
+# If the CALL_GRAPH tag is set to YES then doxygen will generate a call
+# dependency graph for every global function or class method.
+#
+# Note that enabling this option will significantly increase the time of a run.
+# So in most cases it will be better to enable call graphs for selected
+# functions only using the \callgraph command.
+# The default value is: NO.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+CALL_GRAPH             = NO
+
+# If the CALLER_GRAPH tag is set to YES then doxygen will generate a caller
+# dependency graph for every global function or class method.
+#
+# Note that enabling this option will significantly increase the time of a run.
+# So in most cases it will be better to enable caller graphs for selected
+# functions only using the \callergraph command.
+# The default value is: NO.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+CALLER_GRAPH           = NO
+
+# If the GRAPHICAL_HIERARCHY tag is set to YES then doxygen will graphical
+# hierarchy of all classes instead of a textual one.
+# The default value is: YES.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+GRAPHICAL_HIERARCHY    = YES
+
+# If the DIRECTORY_GRAPH tag is set to YES then doxygen will show the
+# dependencies a directory has on other directories in a graphical way. The
+# dependency relations are determined by the #include relations between the
+# files in the directories.
+# The default value is: YES.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+DIRECTORY_GRAPH        = YES
+
+# The DOT_IMAGE_FORMAT tag can be used to set the image format of the images
+# generated by dot.
+# Note: If you choose svg you need to set HTML_FILE_EXTENSION to xhtml in order
+# to make the SVG files visible in IE 9+ (other browsers do not have this
+# requirement).
+# Possible values are: png, png:cairo, png:cairo:cairo, png:cairo:gd, png:gd,
+# png:gd:gd, jpg, jpg:cairo, jpg:cairo:gd, jpg:gd, jpg:gd:gd, gif, gif:cairo,
+# gif:cairo:gd, gif:gd, gif:gd:gd and svg.
+# The default value is: png.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+DOT_IMAGE_FORMAT       = png
+
+# If DOT_IMAGE_FORMAT is set to svg, then this option can be set to YES to
+# enable generation of interactive SVG images that allow zooming and panning.
+#
+# Note that this requires a modern browser other than Internet Explorer. Tested
+# and working are Firefox, Chrome, Safari, and Opera.
+# Note: For IE 9+ you need to set HTML_FILE_EXTENSION to xhtml in order to make
+# the SVG files visible. Older versions of IE do not have SVG support.
+# The default value is: NO.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+INTERACTIVE_SVG        = NO
+
+# The DOT_PATH tag can be used to specify the path where the dot tool can be
+# found. If left blank, it is assumed the dot tool can be found in the path.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+DOT_PATH               =
+
+# The DOTFILE_DIRS tag can be used to specify one or more directories that
+# contain dot files that are included in the documentation (see the \dotfile
+# command).
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+DOTFILE_DIRS           =
+
+# The MSCFILE_DIRS tag can be used to specify one or more directories that
+# contain msc files that are included in the documentation (see the \mscfile
+# command).
+
+MSCFILE_DIRS           =
+
+# The DIAFILE_DIRS tag can be used to specify one or more directories that
+# contain dia files that are included in the documentation (see the \diafile
+# command).
+
+DIAFILE_DIRS           =
+
+# When using plantuml, the PLANTUML_JAR_PATH tag should be used to specify the
+# path where java can find the plantuml.jar file. If left blank, it is assumed
+# PlantUML is not used or called during a preprocessing step. Doxygen will
+# generate a warning when it encounters a \startuml command in this case and
+# will not generate output for the diagram.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+PLANTUML_JAR_PATH      =
+
+# The DOT_GRAPH_MAX_NODES tag can be used to set the maximum number of nodes
+# that will be shown in the graph. If the number of nodes in a graph becomes
+# larger than this value, doxygen will truncate the graph, which is visualized
+# by representing a node as a red box. Note that doxygen if the number of direct
+# children of the root node in a graph is already larger than
+# DOT_GRAPH_MAX_NODES then the graph will not be shown at all. Also note that
+# the size of a graph can be further restricted by MAX_DOT_GRAPH_DEPTH.
+# Minimum value: 0, maximum value: 10000, default value: 50.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+DOT_GRAPH_MAX_NODES    = 50
+
+# The MAX_DOT_GRAPH_DEPTH tag can be used to set the maximum depth of the graphs
+# generated by dot. A depth value of 3 means that only nodes reachable from the
+# root by following a path via at most 3 edges will be shown. Nodes that lay
+# further from the root node will be omitted. Note that setting this option to 1
+# or 2 may greatly reduce the computation time needed for large code bases. Also
+# note that the size of a graph can be further restricted by
+# DOT_GRAPH_MAX_NODES. Using a depth of 0 means no depth restriction.
+# Minimum value: 0, maximum value: 1000, default value: 0.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+MAX_DOT_GRAPH_DEPTH    = 0
+
+# Set the DOT_TRANSPARENT tag to YES to generate images with a transparent
+# background. This is disabled by default, because dot on Windows does not seem
+# to support this out of the box.
+#
+# Warning: Depending on the platform used, enabling this option may lead to
+# badly anti-aliased labels on the edges of a graph (i.e. they become hard to
+# read).
+# The default value is: NO.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+DOT_TRANSPARENT        = NO
+
+# Set the DOT_MULTI_TARGETS tag to YES allow dot to generate multiple output
+# files in one run (i.e. multiple -o and -T options on the command line). This
+# makes dot run faster, but since only newer versions of dot (>1.8.10) support
+# this, this feature is disabled by default.
+# The default value is: NO.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+DOT_MULTI_TARGETS      = NO
+
+# If the GENERATE_LEGEND tag is set to YES doxygen will generate a legend page
+# explaining the meaning of the various boxes and arrows in the dot generated
+# graphs.
+# The default value is: YES.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+GENERATE_LEGEND        = YES
+
+# If the DOT_CLEANUP tag is set to YES doxygen will remove the intermediate dot
+# files that are used to generate the various graphs.
+# The default value is: YES.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+DOT_CLEANUP            = YES
diff --git a/include/protozero/exception.hpp b/include/protozero/exception.hpp
new file mode 100644
index 0000000..1229f7d
--- /dev/null
+++ b/include/protozero/exception.hpp
@@ -0,0 +1,68 @@
+#ifndef PROTOZERO_EXCEPTION_HPP
+#define PROTOZERO_EXCEPTION_HPP
+
+/*****************************************************************************
+
+protozero - Minimalistic protocol buffer decoder and encoder in C++.
+
+This file is from https://github.com/mapbox/protozero where you can find more
+documentation.
+
+*****************************************************************************/
+
+/**
+ * @file exception.hpp
+ *
+ * @brief Contains the exceptions used in the protozero library.
+ */
+
+#include <exception>
+
+/**
+ * @brief All parts of the protozero header-only library are in this namespace.
+ */
+namespace protozero {
+
+/**
+ * All exceptions explicitly thrown by the functions of the protozero library
+ * derive from this exception.
+ */
+struct exception : std::exception {
+    /// Returns the explanatory string.
+    const char *what() const noexcept { return "pbf exception"; }
+};
+
+/**
+ * This exception is thrown when parsing a varint thats larger than allowed.
+ * This should never happen unless the data is corrupted.
+ */
+struct varint_too_long_exception : exception {
+    /// Returns the explanatory string.
+    const char *what() const noexcept { return "varint too long exception"; }
+};
+
+/**
+ * This exception is thrown when the wire type of a pdf field is unknown.
+ * This should never happen unless the data is corrupted.
+ */
+struct unknown_pbf_wire_type_exception : exception {
+    /// Returns the explanatory string.
+    const char *what() const noexcept { return "unknown pbf field type exception"; }
+};
+
+/**
+ * This exception is thrown when we are trying to read a field and there
+ * are not enough bytes left in the buffer to read it. Almost all functions
+ * of the pbf_reader class can throw this exception.
+ *
+ * This should never happen unless the data is corrupted or you have
+ * initialized the pbf_reader object with incomplete data.
+ */
+struct end_of_buffer_exception : exception {
+    /// Returns the explanatory string.
+    const char *what() const noexcept { return "end of buffer exception"; }
+};
+
+} // end namespace protozero
+
+#endif // PROTOZERO_EXCEPTION_HPP
diff --git a/include/protozero/pbf_reader.hpp b/include/protozero/pbf_reader.hpp
new file mode 100644
index 0000000..b9426d4
--- /dev/null
+++ b/include/protozero/pbf_reader.hpp
@@ -0,0 +1,1003 @@
+#ifndef PROTOZERO_PBF_READER_HPP
+#define PROTOZERO_PBF_READER_HPP
+
+/*****************************************************************************
+
+protozero - Minimalistic protocol buffer decoder and encoder in C++.
+
+This file is from https://github.com/mapbox/protozero where you can find more
+documentation.
+
+*****************************************************************************/
+
+/**
+ * @file pbf_reader.hpp
+ *
+ * @brief Contains the pbf_reader class.
+ */
+
+#if __BYTE_ORDER != __LITTLE_ENDIAN
+# error "This code only works on little endian machines."
+#endif
+
+#include <cassert>
+#include <cstddef>
+#include <cstdint>
+#include <cstring>
+#include <iterator>
+#include <string>
+#include <utility>
+
+#include <protozero/pbf_types.hpp>
+#include <protozero/exception.hpp>
+#include <protozero/varint.hpp>
+
+/// Wrapper for assert() used for testing
+#ifndef pbf_assert
+# define pbf_assert(x) assert(x)
+#endif
+
+namespace protozero {
+
+/**
+ * This class represents a protobuf message. Either a top-level message or
+ * a nested sub-message. Top-level messages can be created from any buffer
+ * with a pointer and length:
+ *
+ * @code
+ *    std::string buffer;
+ *    // fill buffer...
+ *    pbf_reader message(buffer.data(), buffer.size());
+ * @endcode
+ *
+ * Sub-messages are created using get_message():
+ *
+ * @code
+ *    pbf_reader message(...);
+ *    message.next();
+ *    pbf_reader submessage = message.get_message();
+ * @endcode
+ *
+ * All methods of the pbf_reader class except get_bytes() and get_string()
+ * provide the strong exception guarantee, ie they either succeed or do not
+ * change the pbf_reader object they are called on. Use the get_data() method
+ * instead of get_bytes() or get_string(), if you need this guarantee.
+ */
+class pbf_reader {
+
+    // A pointer to the next unread data.
+    const char *m_data = nullptr;
+
+    // A pointer to one past the end of data.
+    const char *m_end = nullptr;
+
+    // The wire type of the current field.
+    pbf_wire_type m_wire_type = pbf_wire_type::unknown;
+
+    // The tag of the current field.
+    pbf_tag_type m_tag = 0;
+
+    template <typename T> inline T get_fixed();
+    template <typename T> inline T get_varint();
+    template <typename T> inline T get_svarint();
+    template <typename T> inline std::pair<const T*, const T*> packed_fixed();
+
+    inline pbf_length_type get_length() { return get_varint<pbf_length_type>(); }
+
+    inline void skip_bytes(pbf_length_type len);
+
+    inline pbf_length_type get_len_and_skip();
+
+public:
+
+    /**
+     * Construct a pbf_reader message from a data pointer and a length. The pointer
+     * will be stored inside the pbf_reader object, no data is copied. So you must
+     * make sure the buffer stays valid as long as the pbf_reader object is used.
+     *
+     * The buffer must contain a complete protobuf message.
+     *
+     * @post There is no current field.
+     */
+    inline pbf_reader(const char *data, size_t length) noexcept;
+
+    /**
+     * Construct a pbf_reader message from a data pointer and a length. The pointer
+     * will be stored inside the pbf_reader object, no data is copied. So you must
+     * make sure the buffer stays valid as long as the pbf_reader object is used.
+     *
+     * The buffer must contain a complete protobuf message.
+     *
+     * @post There is no current field.
+     */
+    inline pbf_reader(std::pair<const char *, size_t> data) noexcept;
+
+    /**
+     * Construct a pbf_reader message from a std::string. A pointer to the string
+     * internals will be stored inside the pbf_reader object, no data is copied.
+     * So you must make sure the string is unchanged as long as the pbf_reader
+     * object is used.
+     *
+     * The string must contain a complete protobuf message.
+     *
+     * @post There is no current field.
+     */
+    inline pbf_reader(const std::string& data) noexcept;
+
+    /**
+     * pbf_reader can be default constructed and behaves like it has an empty
+     * buffer.
+     */
+    inline pbf_reader() noexcept = default;
+
+    /// pbf_reader messages can be copied trivially.
+    inline pbf_reader(const pbf_reader&) noexcept = default;
+
+    /// pbf_reader messages can be moved trivially.
+    inline pbf_reader(pbf_reader&&) noexcept = default;
+
+    /// pbf_reader messages can be copied trivially.
+    inline pbf_reader& operator=(const pbf_reader& other) noexcept = default;
+
+    /// pbf_reader messages can be moved trivially.
+    inline pbf_reader& operator=(pbf_reader&& other) noexcept = default;
+
+    inline ~pbf_reader() = default;
+
+    /**
+     * In a boolean context the pbf_reader class evaluates to `true` if there are
+     * still fields available and to `false` if the last field has been read.
+     */
+    inline operator bool() const noexcept;
+
+    /**
+     * Return the length in bytes of the current message. If you have
+     * already called next() and/or any of the get_*() functions, this will
+     * return the remaining length.
+     *
+     * This can, for instance, be used to estimate the space needed for a
+     * buffer. Of course you have to know reasonably well what data to expect
+     * and how it is encoded for this number to have any meaning.
+     */
+    size_t length() const noexcept {
+        return size_t(m_end - m_data);
+    }
+
+    /**
+     * Set next field in the message as the current field. This is usually
+     * called in a while loop:
+     *
+     * @code
+     *    pbf_reader message(...);
+     *    while (message.next()) {
+     *        // handle field
+     *    }
+     * @endcode
+     *
+     * @returns `true` if there is a next field, `false` if not.
+     * @pre There must be no current field.
+     * @post If it returns `true` there is a current field now.
+     */
+    inline bool next();
+
+    /**
+     * Set next field with given tag in the message as the current field.
+     * Fields with other tags are skipped. This is usually called in a while
+     * loop for repeated fields:
+     *
+     * @code
+     *    pbf_reader message(...);
+     *    while (message.next(17)) {
+     *        // handle field
+     *    }
+     * @endcode
+     *
+     * or you can call it just once to get the one field with this tag:
+     *
+     * @code
+     *    pbf_reader message(...);
+     *    if (message.next(17)) {
+     *        // handle field
+     *    }
+     * @endcode
+     *
+     * @returns `true` if there is a next field with this tag.
+     * @pre There must be no current field.
+     * @post If it returns `true` there is a current field now with the given tag.
+     */
+    inline bool next(pbf_tag_type tag);
+
+    /**
+     * The tag of the current field. The tag is the field number from the
+     * description in the .proto file.
+     *
+     * Call next() before calling this function to set the current field.
+     *
+     * @returns tag of the current field.
+     * @pre There must be a current field (ie. next() must have returned `true`).
+     */
+    inline pbf_tag_type tag() const noexcept;
+
+    /**
+     * Get the wire type of the current field. The wire types are:
+     *
+     * * 0 - varint
+     * * 1 - 64 bit
+     * * 2 - length-delimited
+     * * 5 - 32 bit
+     *
+     * All other types are illegal.
+     *
+     * Call next() before calling this function to set the current field.
+     *
+     * @returns wire type of the current field.
+     * @pre There must be a current field (ie. next() must have returned `true`).
+     */
+    inline pbf_wire_type wire_type() const noexcept;
+
+    /**
+     * Check the wire type of the current field.
+     *
+     * @returns `true` if the current field has the given wire type.
+     * @pre There must be a current field (ie. next() must have returned `true`).
+     */
+    inline bool has_wire_type(pbf_wire_type type) const noexcept;
+
+    /**
+     * Consume the current field.
+     *
+     * @pre There must be a current field (ie. next() must have returned `true`).
+     * @post The current field was consumed and there is no current field now.
+     */
+    inline void skip();
+
+    ///@{
+    /**
+     * @name Scalar field accessor functions
+     */
+
+    /**
+     * Consume and return value of current "bool" field.
+     *
+     * @pre There must be a current field (ie. next() must have returned `true`).
+     * @pre The current field must be of type "bool".
+     * @post The current field was consumed and there is no current field now.
+     */
+    inline bool get_bool();
+
+    /**
+     * Consume and return value of current "enum" field.
+     *
+     * @pre There must be a current field (ie. next() must have returned `true`).
+     * @pre The current field must be of type "enum".
+     * @post The current field was consumed and there is no current field now.
+     */
+    inline int32_t get_enum() {
+        pbf_assert(has_wire_type(pbf_wire_type::varint) && "not a varint");
+        return get_varint<int32_t>();
+    }
+
+    /**
+     * Consume and return value of current "int32" varint field.
+     *
+     * @pre There must be a current field (ie. next() must have returned `true`).
+     * @pre The current field must be of type "int32".
+     * @post The current field was consumed and there is no current field now.
+     */
+    inline int32_t get_int32() {
+        pbf_assert(has_wire_type(pbf_wire_type::varint) && "not a varint");
+        return get_varint<int32_t>();
+    }
+
+    /**
+     * Consume and return value of current "sint32" varint field.
+     *
+     * @pre There must be a current field (ie. next() must have returned `true`).
+     * @pre The current field must be of type "sint32".
+     * @post The current field was consumed and there is no current field now.
+     */
+    inline int32_t get_sint32() {
+        pbf_assert(has_wire_type(pbf_wire_type::varint) && "not a varint");
+        return get_svarint<int32_t>();
+    }
+
+    /**
+     * Consume and return value of current "uint32" varint field.
+     *
+     * @pre There must be a current field (ie. next() must have returned `true`).
+     * @pre The current field must be of type "uint32".
+     * @post The current field was consumed and there is no current field now.
+     */
+    inline uint32_t get_uint32() {
+        pbf_assert(has_wire_type(pbf_wire_type::varint) && "not a varint");
+        return get_varint<uint32_t>();
+    }
+
+    /**
+     * Consume and return value of current "int64" varint field.
+     *
+     * @pre There must be a current field (ie. next() must have returned `true`).
+     * @pre The current field must be of type "int64".
+     * @post The current field was consumed and there is no current field now.
+     */
+    inline int64_t get_int64() {
+        pbf_assert(has_wire_type(pbf_wire_type::varint) && "not a varint");
+        return get_varint<int64_t>();
+    }
+
+    /**
+     * Consume and return value of current "sint64" varint field.
+     *
+     * @pre There must be a current field (ie. next() must have returned `true`).
+     * @pre The current field must be of type "sint64".
+     * @post The current field was consumed and there is no current field now.
+     */
+    inline int64_t get_sint64() {
+        pbf_assert(has_wire_type(pbf_wire_type::varint) && "not a varint");
+        return get_svarint<int64_t>();
+    }
+
+    /**
+     * Consume and return value of current "uint64" varint field.
+     *
+     * @pre There must be a current field (ie. next() must have returned `true`).
+     * @pre The current field must be of type "uint64".
+     * @post The current field was consumed and there is no current field now.
+     */
+    inline uint64_t get_uint64() {
+        pbf_assert(has_wire_type(pbf_wire_type::varint) && "not a varint");
+        return get_varint<uint64_t>();
+    }
+
+    /**
+     * Consume and return value of current "fixed32" field.
+     *
+     * @pre There must be a current field (ie. next() must have returned `true`).
+     * @pre The current field must be of type "fixed32".
+     * @post The current field was consumed and there is no current field now.
+     */
+    inline uint32_t get_fixed32();
+
+    /**
+     * Consume and return value of current "sfixed32" field.
+     *
+     * @pre There must be a current field (ie. next() must have returned `true`).
+     * @pre The current field must be of type "sfixed32".
+     * @post The current field was consumed and there is no current field now.
+     */
+    inline int32_t get_sfixed32();
+
+    /**
+     * Consume and return value of current "fixed64" field.
+     *
+     * @pre There must be a current field (ie. next() must have returned `true`).
+     * @pre The current field must be of type "fixed64".
+     * @post The current field was consumed and there is no current field now.
+     */
+    inline uint64_t get_fixed64();
+
+    /**
+     * Consume and return value of current "sfixed64" field.
+     *
+     * @pre There must be a current field (ie. next() must have returned `true`).
+     * @pre The current field must be of type "sfixed64".
+     * @post The current field was consumed and there is no current field now.
+     */
+    inline int64_t get_sfixed64();
+
+    /**
+     * Consume and return value of current "float" field.
+     *
+     * @pre There must be a current field (ie. next() must have returned `true`).
+     * @pre The current field must be of type "float".
+     * @post The current field was consumed and there is no current field now.
+     */
+    inline float get_float();
+
+    /**
+     * Consume and return value of current "double" field.
+     *
+     * @pre There must be a current field (ie. next() must have returned `true`).
+     * @pre The current field must be of type "double".
+     * @post The current field was consumed and there is no current field now.
+     */
+    inline double get_double();
+
+    /**
+     * Consume and return value of current "bytes" or "string" field.
+     *
+     * @returns A pair with a pointer to the data and the length of the data.
+     * @pre There must be a current field (ie. next() must have returned `true`).
+     * @pre The current field must be of type "bytes" or "string".
+     * @post The current field was consumed and there is no current field now.
+     */
+    inline std::pair<const char*, pbf_length_type> get_data();
+
+    /**
+     * Consume and return value of current "bytes" field.
+     *
+     * @pre There must be a current field (ie. next() must have returned `true`).
+     * @pre The current field must be of type "bytes".
+     * @post The current field was consumed and there is no current field now.
+     */
+    inline std::string get_bytes();
+
+    /**
+     * Consume and return value of current "string" field.
+     *
+     * @pre There must be a current field (ie. next() must have returned `true`).
+     * @pre The current field must be of type "string".
+     * @post The current field was consumed and there is no current field now.
+     */
+    inline std::string get_string();
+
+    /**
+     * Consume and return value of current "message" field.
+     *
+     * @pre There must be a current field (ie. next() must have returned `true`).
+     * @pre The current field must be of type "message".
+     * @post The current field was consumed and there is no current field now.
+     */
+    inline pbf_reader get_message() {
+        return pbf_reader(get_data());
+    }
+
+    ///@}
+
+private:
+
+    template <typename T>
+    class const_varint_iterator : public std::iterator<std::forward_iterator_tag, T> {
+
+    protected:
+
+        const char* m_data;
+        const char* m_end;
+
+    public:
+
+        const_varint_iterator() noexcept :
+            m_data(nullptr),
+            m_end(nullptr) {
+        }
+
+        const_varint_iterator(const char *data, const char* end) noexcept :
+            m_data(data),
+            m_end(end) {
+        }
+
+        const_varint_iterator(const const_varint_iterator&) noexcept = default;
+        const_varint_iterator(const_varint_iterator&&) noexcept = default;
+
+        const_varint_iterator& operator=(const const_varint_iterator&) noexcept = default;
+        const_varint_iterator& operator=(const_varint_iterator&&) noexcept = default;
+
+        ~const_varint_iterator() noexcept = default;
+
+        T operator*() {
+            const char* d = m_data; // will be thrown away
+            return static_cast<T>(decode_varint(&d, m_end));
+        }
+
+        const_varint_iterator& operator++() {
+            // Ignore the result, we call decode_varint() just for the
+            // side-effect of updating m_data.
+            decode_varint(&m_data, m_end);
+            return *this;
+        }
+
+        const_varint_iterator operator++(int) {
+            const const_varint_iterator tmp(*this);
+            ++(*this);
+            return tmp;
+        }
+
+        bool operator==(const const_varint_iterator& rhs) const noexcept {
+            return m_data == rhs.m_data && m_end == rhs.m_end;
+        }
+
+        bool operator!=(const const_varint_iterator& rhs) const noexcept {
+            return !(*this == rhs);
+        }
+
+    }; // class const_varint_iterator
+
+    template <typename T>
+    class const_svarint_iterator : public const_varint_iterator<T> {
+
+    public:
+
+        const_svarint_iterator() noexcept :
+            const_varint_iterator<T>() {
+        }
+
+        const_svarint_iterator(const char *data, const char* end) noexcept :
+            const_varint_iterator<T>(data, end) {
+        }
+
+        const_svarint_iterator(const const_svarint_iterator&) = default;
+        const_svarint_iterator(const_svarint_iterator&&) = default;
+
+        const_svarint_iterator& operator=(const const_svarint_iterator&) = default;
+        const_svarint_iterator& operator=(const_svarint_iterator&&) = default;
+
+        ~const_svarint_iterator() = default;
+
+        T operator*() {
+            const char* d = this->m_data; // will be thrown away
+            return static_cast<T>(decode_zigzag64(decode_varint(&d, this->m_end)));
+        }
+
+        const_svarint_iterator& operator++() {
+            // Ignore the result, we call decode_varint() just for the
+            // side-effect of updating m_data.
+            decode_varint(&this->m_data, this->m_end);
+            return *this;
+        }
+
+        const_svarint_iterator operator++(int) {
+            const const_svarint_iterator tmp(*this);
+            ++(*this);
+            return tmp;
+        }
+
+    }; // class const_svarint_iterator
+
+public:
+
+    /// Forward iterator for iterating over bool (int32 varint) values.
+    typedef const_varint_iterator< int32_t> const_bool_iterator;
+
+    /// Forward iterator for iterating over enum (int32 varint) values.
+    typedef const_varint_iterator< int32_t> const_enum_iterator;
+
+    /// Forward iterator for iterating over int32 (varint) values.
+    typedef const_varint_iterator< int32_t> const_int32_iterator;
+
+    /// Forward iterator for iterating over sint32 (varint) values.
+    typedef const_svarint_iterator<int32_t> const_sint32_iterator;
+
+    /// Forward iterator for iterating over uint32 (varint) values.
+    typedef const_varint_iterator<uint32_t> const_uint32_iterator;
+
+    /// Forward iterator for iterating over int64 (varint) values.
+    typedef const_varint_iterator< int64_t> const_int64_iterator;
+
+    /// Forward iterator for iterating over sint64 (varint) values.
+    typedef const_svarint_iterator<int64_t> const_sint64_iterator;
+
+    /// Forward iterator for iterating over uint64 (varint) values.
+    typedef const_varint_iterator<uint64_t> const_uint64_iterator;
+
+    ///@{
+    /**
+     * @name Repeated packed field accessor functions
+     */
+
+    /**
+     * Consume current "repeated packed bool" field.
+     *
+     * @returns a pair of iterators to the beginning and one past the end of
+     *          the data.
+     * @pre There must be a current field (ie. next() must have returned `true`).
+     * @pre The current field must be of type "repeated packed bool".
+     * @post The current field was consumed and there is no current field now.
+     */
+    inline std::pair<pbf_reader::const_bool_iterator, pbf_reader::const_bool_iterator> get_packed_bool();
+
+    /**
+     * Consume current "repeated packed enum" field.
+     *
+     * @returns a pair of iterators to the beginning and one past the end of
+     *          the data.
+     * @pre There must be a current field (ie. next() must have returned `true`).
+     * @pre The current field must be of type "repeated packed enum".
+     * @post The current field was consumed and there is no current field now.
+     */
+    inline std::pair<pbf_reader::const_enum_iterator, pbf_reader::const_enum_iterator> get_packed_enum();
+
+    /**
+     * Consume current "repeated packed int32" field.
+     *
+     * @returns a pair of iterators to the beginning and one past the end of
+     *          the data.
+     * @pre There must be a current field (ie. next() must have returned `true`).
+     * @pre The current field must be of type "repeated packed int32".
+     * @post The current field was consumed and there is no current field now.
+     */
+    inline std::pair<pbf_reader::const_int32_iterator, pbf_reader::const_int32_iterator> get_packed_int32();
+
+    /**
+     * Consume current "repeated packed sint32" field.
+     *
+     * @returns a pair of iterators to the beginning and one past the end of
+     *          the data.
+     * @pre There must be a current field (ie. next() must have returned `true`).
+     * @pre The current field must be of type "repeated packed sint32".
+     * @post The current field was consumed and there is no current field now.
+     */
+    inline std::pair<pbf_reader::const_sint32_iterator, pbf_reader::const_sint32_iterator> get_packed_sint32();
+
+    /**
+     * Consume current "repeated packed uint32" field.
+     *
+     * @returns a pair of iterators to the beginning and one past the end of
+     *          the data.
+     * @pre There must be a current field (ie. next() must have returned `true`).
+     * @pre The current field must be of type "repeated packed uint32".
+     * @post The current field was consumed and there is no current field now.
+     */
+    inline std::pair<pbf_reader::const_uint32_iterator, pbf_reader::const_uint32_iterator> get_packed_uint32();
+
+    /**
+     * Consume current "repeated packed int64" field.
+     *
+     * @returns a pair of iterators to the beginning and one past the end of
+     *          the data.
+     * @pre There must be a current field (ie. next() must have returned `true`).
+     * @pre The current field must be of type "repeated packed int64".
+     * @post The current field was consumed and there is no current field now.
+     */
+    inline std::pair<pbf_reader::const_int64_iterator, pbf_reader::const_int64_iterator> get_packed_int64();
+
+    /**
+     * Consume current "repeated packed sint64" field.
+     *
+     * @returns a pair of iterators to the beginning and one past the end of
+     *          the data.
+     * @pre There must be a current field (ie. next() must have returned `true`).
+     * @pre The current field must be of type "repeated packed sint64".
+     * @post The current field was consumed and there is no current field now.
+     */
+    inline std::pair<pbf_reader::const_sint64_iterator, pbf_reader::const_sint64_iterator> get_packed_sint64();
+
+    /**
+     * Consume current "repeated packed uint64" field.
+     *
+     * @returns a pair of iterators to the beginning and one past the end of
+     *          the data.
+     * @pre There must be a current field (ie. next() must have returned `true`).
+     * @pre The current field must be of type "repeated packed uint64".
+     * @post The current field was consumed and there is no current field now.
+     */
+    inline std::pair<pbf_reader::const_uint64_iterator, pbf_reader::const_uint64_iterator> get_packed_uint64();
+
+    /**
+     * Consume current "repeated packed fixed32" field.
+     *
+     * @returns a pair of iterators to the beginning and one past the end of
+     *          the data.
+     * @pre There must be a current field (ie. next() must have returned `true`).
+     * @pre The current field must be of type "repeated packed fixed32".
+     * @post The current field was consumed and there is no current field now.
+     */
+    inline std::pair<const uint32_t*, const uint32_t*> get_packed_fixed32();
+
+    /**
+     * Consume current "repeated packed sfixed32" field.
+     *
+     * @returns a pair of iterators to the beginning and one past the end of
+     *          the data.
+     * @pre There must be a current field (ie. next() must have returned `true`).
+     * @pre The current field must be of type "repeated packed sfixed32".
+     * @post The current field was consumed and there is no current field now.
+     */
+    inline std::pair<const int32_t*, const int32_t*> get_packed_sfixed32();
+
+    /**
+     * Consume current "repeated packed fixed64" field.
+     *
+     * @returns a pair of iterators to the beginning and one past the end of
+     *          the data.
+     * @pre There must be a current field (ie. next() must have returned `true`).
+     * @pre The current field must be of type "repeated packed fixed64".
+     * @post The current field was consumed and there is no current field now.
+     */
+    inline std::pair<const uint64_t*, const uint64_t*> get_packed_fixed64();
+
+    /**
+     * Consume current "repeated packed sfixed64" field.
+     *
+     * @returns a pair of iterators to the beginning and one past the end of
+     *          the data.
+     * @pre There must be a current field (ie. next() must have returned `true`).
+     * @pre The current field must be of type "repeated packed sfixed64".
+     * @post The current field was consumed and there is no current field now.
+     */
+    inline std::pair<const int64_t*, const int64_t*> get_packed_sfixed64();
+
+    /**
+     * Consume current "repeated packed float" field.
+     *
+     * @returns a pair of iterators to the beginning and one past the end of
+     *          the data.
+     * @pre There must be a current field (ie. next() must have returned `true`).
+     * @pre The current field must be of type "repeated packed float".
+     * @post The current field was consumed and there is no current field now.
+     */
+    inline std::pair<const float*, const float*> get_packed_float();
+
+    /**
+     * Consume current "repeated packed double" field.
+     *
+     * @returns a pair of iterators to the beginning and one past the end of
+     *          the data.
+     * @pre There must be a current field (ie. next() must have returned `true`).
+     * @pre The current field must be of type "repeated packed double".
+     * @post The current field was consumed and there is no current field now.
+     */
+    inline std::pair<const double*, const double*> get_packed_double();
+
+    ///@}
+
+}; // class pbf_reader
+
+pbf_reader::pbf_reader(const char *data, size_t length) noexcept
+    : m_data(data),
+      m_end(data + length),
+      m_wire_type(pbf_wire_type::unknown),
+      m_tag(0) {
+}
+
+pbf_reader::pbf_reader(std::pair<const char *, size_t> data) noexcept
+    : m_data(data.first),
+      m_end(data.first + data.second),
+      m_wire_type(pbf_wire_type::unknown),
+      m_tag(0) {
+}
+
+pbf_reader::pbf_reader(const std::string& data) noexcept
+    : m_data(data.data()),
+      m_end(data.data() + data.size()),
+      m_wire_type(pbf_wire_type::unknown),
+      m_tag(0) {
+}
+
+pbf_reader::operator bool() const noexcept {
+    return m_data < m_end;
+}
+
+bool pbf_reader::next() {
+    if (m_data == m_end) {
+        return false;
+    }
+
+    auto value = get_varint<uint32_t>();
+    m_tag = value >> 3;
+
+    // tags 0 and 19000 to 19999 are not allowed as per
+    // https://developers.google.com/protocol-buffers/docs/proto
+    pbf_assert(((m_tag > 0 && m_tag < 19000) || (m_tag > 19999 && m_tag <= ((1 << 29) - 1))) && "tag out of range");
+
+    m_wire_type = pbf_wire_type(value & 0x07);
+// XXX do we want this check? or should it throw an exception?
+//        pbf_assert((m_wire_type <=2 || m_wire_type == 5) && "illegal wire type");
+    return true;
+}
+
+bool pbf_reader::next(pbf_tag_type requested_tag) {
+    while (next()) {
+        if (m_tag == requested_tag) {
+            return true;
+        } else {
+            skip();
+        }
+    }
+    return false;
+}
+
+pbf_tag_type pbf_reader::tag() const noexcept {
+    return m_tag;
+}
+
+pbf_wire_type pbf_reader::wire_type() const noexcept {
+    return m_wire_type;
+}
+
+bool pbf_reader::has_wire_type(pbf_wire_type type) const noexcept {
+    return wire_type() == type;
+}
+
+void pbf_reader::skip_bytes(pbf_length_type len) {
+    if (m_data + len > m_end) {
+        throw end_of_buffer_exception();
+    }
+    m_data += len;
+
+// In debug builds reset the tag to zero so that we can detect (some)
+// wrong code.
+#ifndef NDEBUG
+    m_tag = 0;
+#endif
+}
+
+void pbf_reader::skip() {
+    pbf_assert(tag() != 0 && "call next() before calling skip()");
+    switch (wire_type()) {
+        case pbf_wire_type::varint:
+            (void)get_uint32(); // called for the side-effect of skipping value
+            break;
+        case pbf_wire_type::fixed64:
+            skip_bytes(8);
+            break;
+        case pbf_wire_type::length_delimited:
+            skip_bytes(get_length());
+            break;
+        case pbf_wire_type::fixed32:
+            skip_bytes(4);
+            break;
+        default:
+            throw unknown_pbf_wire_type_exception();
+    }
+}
+
+pbf_length_type pbf_reader::get_len_and_skip() {
+    auto len = get_length();
+    skip_bytes(len);
+    return len;
+}
+
+template <typename T>
+T pbf_reader::get_varint() {
+    return static_cast<T>(decode_varint(&m_data, m_end));
+}
+
+template <typename T>
+T pbf_reader::get_svarint() {
+    pbf_assert((has_wire_type(pbf_wire_type::varint) || has_wire_type(pbf_wire_type::length_delimited)) && "not a varint");
+    return static_cast<T>(decode_zigzag64(decode_varint(&m_data, m_end)));
+}
+
+template <typename T>
+T pbf_reader::get_fixed() {
+    T result;
+    skip_bytes(sizeof(T));
+    memcpy(&result, m_data - sizeof(T), sizeof(T));
+    return result;
+}
+
+uint32_t pbf_reader::get_fixed32() {
+    pbf_assert(tag() != 0 && "call next() before accessing field value");
+    pbf_assert(has_wire_type(pbf_wire_type::fixed32) && "not a 32-bit fixed");
+    return get_fixed<uint32_t>();
+}
+
+int32_t pbf_reader::get_sfixed32() {
+    pbf_assert(tag() != 0 && "call next() before accessing field value");
+    pbf_assert(has_wire_type(pbf_wire_type::fixed32) && "not a 32-bit fixed");
+    return get_fixed<int32_t>();
+}
+
+uint64_t pbf_reader::get_fixed64() {
+    pbf_assert(tag() != 0 && "call next() before accessing field value");
+    pbf_assert(has_wire_type(pbf_wire_type::fixed64) && "not a 64-bit fixed");
+    return get_fixed<uint64_t>();
+}
+
+int64_t pbf_reader::get_sfixed64() {
+    pbf_assert(tag() != 0 && "call next() before accessing field value");
+    pbf_assert(has_wire_type(pbf_wire_type::fixed64) && "not a 64-bit fixed");
+    return get_fixed<int64_t>();
+}
+
+float pbf_reader::get_float() {
+    pbf_assert(tag() != 0 && "call next() before accessing field value");
+    pbf_assert(has_wire_type(pbf_wire_type::fixed32) && "not a 32-bit fixed");
+    return get_fixed<float>();
+}
+
+double pbf_reader::get_double() {
+    pbf_assert(tag() != 0 && "call next() before accessing field value");
+    pbf_assert(has_wire_type(pbf_wire_type::fixed64) && "not a 64-bit fixed");
+    return get_fixed<double>();
+}
+
+bool pbf_reader::get_bool() {
+    pbf_assert(tag() != 0 && "call next() before accessing field value");
+    pbf_assert(has_wire_type(pbf_wire_type::varint) && "not a varint");
+    pbf_assert((*m_data & 0x80) == 0 && "not a 1 byte varint");
+    skip_bytes(1);
+    return m_data[-1] != 0; // -1 okay because we incremented m_data the line before
+}
+
+std::pair<const char*, pbf_length_type> pbf_reader::get_data() {
+    pbf_assert(tag() != 0 && "call next() before accessing field value");
+    pbf_assert(has_wire_type(pbf_wire_type::length_delimited) && "not of type string, bytes or message");
+    auto len = get_len_and_skip();
+    return std::make_pair(m_data-len, len);
+}
+
+std::string pbf_reader::get_bytes() {
+    auto d = get_data();
+    return std::string(d.first, d.second);
+}
+
+std::string pbf_reader::get_string() {
+    return get_bytes();
+}
+
+template <typename T>
+std::pair<const T*, const T*> pbf_reader::packed_fixed() {
+    pbf_assert(tag() != 0 && "call next() before accessing field value");
+    auto len = get_len_and_skip();
+    pbf_assert(len % sizeof(T) == 0);
+    return std::make_pair(reinterpret_cast<const T*>(m_data-len), reinterpret_cast<const T*>(m_data));
+}
+
+std::pair<const uint32_t*, const uint32_t*> pbf_reader::get_packed_fixed32() {
+    return packed_fixed<uint32_t>();
+}
+
+std::pair<const uint64_t*, const uint64_t*> pbf_reader::get_packed_fixed64() {
+    return packed_fixed<uint64_t>();
+}
+
+std::pair<const int32_t*, const int32_t*> pbf_reader::get_packed_sfixed32() {
+    return packed_fixed<int32_t>();
+}
+
+std::pair<const int64_t*, const int64_t*> pbf_reader::get_packed_sfixed64() {
+    return packed_fixed<int64_t>();
+}
+
+std::pair<const float*, const float*> pbf_reader::get_packed_float() {
+    return packed_fixed<float>();
+}
+
+std::pair<const double*, const double*> pbf_reader::get_packed_double() {
+    return packed_fixed<double>();
+}
+
+std::pair<pbf_reader::const_bool_iterator, pbf_reader::const_bool_iterator> pbf_reader::get_packed_bool() {
+    return get_packed_int32();
+}
+
+std::pair<pbf_reader::const_enum_iterator, pbf_reader::const_enum_iterator> pbf_reader::get_packed_enum() {
+    return get_packed_int32();
+}
+
+std::pair<pbf_reader::const_int32_iterator, pbf_reader::const_int32_iterator> pbf_reader::get_packed_int32() {
+    pbf_assert(tag() != 0 && "call next() before accessing field value");
+    auto len = get_len_and_skip();
+    return std::make_pair(pbf_reader::const_int32_iterator(m_data-len, m_data),
+                          pbf_reader::const_int32_iterator(m_data, m_data));
+}
+
+std::pair<pbf_reader::const_uint32_iterator, pbf_reader::const_uint32_iterator> pbf_reader::get_packed_uint32() {
+    pbf_assert(tag() != 0 && "call next() before accessing field value");
+    auto len = get_len_and_skip();
+    return std::make_pair(pbf_reader::const_uint32_iterator(m_data-len, m_data),
+                          pbf_reader::const_uint32_iterator(m_data, m_data));
+}
+
+std::pair<pbf_reader::const_sint32_iterator, pbf_reader::const_sint32_iterator> pbf_reader::get_packed_sint32() {
+    pbf_assert(tag() != 0 && "call next() before accessing field value");
+    auto len = get_len_and_skip();
+    return std::make_pair(pbf_reader::const_sint32_iterator(m_data-len, m_data),
+                          pbf_reader::const_sint32_iterator(m_data, m_data));
+}
+
+std::pair<pbf_reader::const_int64_iterator, pbf_reader::const_int64_iterator> pbf_reader::get_packed_int64() {
+    pbf_assert(tag() != 0 && "call next() before accessing field value");
+    auto len = get_len_and_skip();
+    return std::make_pair(pbf_reader::const_int64_iterator(m_data-len, m_data),
+                          pbf_reader::const_int64_iterator(m_data, m_data));
+}
+
+std::pair<pbf_reader::const_uint64_iterator, pbf_reader::const_uint64_iterator> pbf_reader::get_packed_uint64() {
+    pbf_assert(tag() != 0 && "call next() before accessing field value");
+    auto len = get_len_and_skip();
+    return std::make_pair(pbf_reader::const_uint64_iterator(m_data-len, m_data),
+                          pbf_reader::const_uint64_iterator(m_data, m_data));
+}
+
+std::pair<pbf_reader::const_sint64_iterator, pbf_reader::const_sint64_iterator> pbf_reader::get_packed_sint64() {
+    pbf_assert(tag() != 0 && "call next() before accessing field value");
+    auto len = get_len_and_skip();
+    return std::make_pair(pbf_reader::const_sint64_iterator(m_data-len, m_data),
+                          pbf_reader::const_sint64_iterator(m_data, m_data));
+}
+
+} // end namespace protozero
+
+#endif // PROTOZERO_PBF_READER_HPP
diff --git a/include/protozero/pbf_types.hpp b/include/protozero/pbf_types.hpp
new file mode 100644
index 0000000..9f38584
--- /dev/null
+++ b/include/protozero/pbf_types.hpp
@@ -0,0 +1,49 @@
+#ifndef PROTOZERO_PBF_TYPES_HPP
+#define PROTOZERO_PBF_TYPES_HPP
+
+/*****************************************************************************
+
+protozero - Minimalistic protocol buffer decoder and encoder in C++.
+
+This file is from https://github.com/mapbox/protozero where you can find more
+documentation.
+
+*****************************************************************************/
+
+/**
+ * @file pbf_types.hpp
+ *
+ * @brief Contains the declaration of low-level types used in the pbf format.
+ */
+
+#include <cstdint>
+
+namespace protozero {
+
+    /**
+     * The type used for field tags (field numbers).
+     */
+    typedef uint32_t pbf_tag_type;
+
+    /**
+     * The type used to encode type information.
+     * See the table on
+     *    https://developers.google.com/protocol-buffers/docs/encoding
+     */
+    enum class pbf_wire_type : uint32_t {
+        varint           = 0, // int32/64, uint32/64, sint32/64, bool, enum
+        fixed64          = 1, // fixed64, sfixed64, double
+        length_delimited = 2, // string, bytes, embedded messages,
+                              // packed repeated fields
+        fixed32          = 5, // fixed32, sfixed32, float
+        unknown          = 99 // used for default setting in this library
+    };
+
+    /**
+     * The type used for length values, such as the length of a field.
+     */
+    typedef uint32_t pbf_length_type;
+
+} // end namespace protozero
+
+#endif // PROTOZERO_PBF_TYPES_HPP
diff --git a/include/protozero/pbf_writer.hpp b/include/protozero/pbf_writer.hpp
new file mode 100644
index 0000000..14b0856
--- /dev/null
+++ b/include/protozero/pbf_writer.hpp
@@ -0,0 +1,657 @@
+#ifndef PROTOZERO_PBF_WRITER_HPP
+#define PROTOZERO_PBF_WRITER_HPP
+
+/*****************************************************************************
+
+protozero - Minimalistic protocol buffer decoder and encoder in C++.
+
+This file is from https://github.com/mapbox/protozero where you can find more
+documentation.
+
+*****************************************************************************/
+
+/**
+ * @file pbf_writer.hpp
+ *
+ * @brief Contains the pbf_writer class.
+ */
+
+#if __BYTE_ORDER != __LITTLE_ENDIAN
+# error "This code only works on little endian machines."
+#endif
+
+#include <cassert>
+#include <cstddef>
+#include <cstdint>
+#include <cstring>
+#include <iterator>
+#include <string>
+
+#include <protozero/pbf_types.hpp>
+#include <protozero/varint.hpp>
+
+/// Wrapper for assert() used for testing
+#ifndef pbf_assert
+# define pbf_assert(x) assert(x)
+#endif
+
+namespace protozero {
+
+/**
+ * The pbf_writer is used to write PBF formatted messages into a buffer.
+ *
+ * Almost all methods in this class can throw an std::bad_alloc exception if
+ * the std::string used as a buffer wants to resize.
+ */
+class pbf_writer {
+
+    std::string* m_data;
+    pbf_writer* m_parent_writer;
+    size_t m_pos = 0;
+
+    inline void add_varint(uint64_t value) {
+        pbf_assert(m_pos == 0 && "you can't add fields to a parent pbf_writer if there is an existing pbf_writer for a submessage");
+        pbf_assert(m_data);
+        write_varint(std::back_inserter(*m_data), value);
+    }
+
+    inline void add_field(pbf_tag_type tag, pbf_wire_type type) {
+        pbf_assert(((tag > 0 && tag < 19000) || (tag > 19999 && tag <= ((1 << 29) - 1))) && "tag out of range");
+        uint32_t b = (tag << 3) | uint32_t(type);
+        add_varint(b);
+    }
+
+    inline void add_tagged_varint(pbf_tag_type tag, uint64_t value) {
+        add_field(tag, pbf_wire_type::varint);
+        add_varint(value);
+    }
+
+    template <typename T>
+    inline void add_fixed(T value) {
+        pbf_assert(m_pos == 0 && "you can't add fields to a parent pbf_writer if there is an existing pbf_writer for a submessage");
+        pbf_assert(m_data);
+        m_data->append(reinterpret_cast<const char*>(&value), sizeof(T));
+    }
+
+    template <typename T, typename It>
+    inline void add_packed_fixed(pbf_tag_type tag, It first, It last, std::input_iterator_tag) {
+        if (first == last) {
+            return;
+        }
+
+        pbf_writer sw(*this, tag);
+
+        while (first != last) {
+            sw.add_fixed<T>(*first++);
+        }
+    }
+
+    template <typename T, typename It>
+    inline void add_packed_fixed(pbf_tag_type tag, It first, It last, std::forward_iterator_tag) {
+        if (first == last) {
+            return;
+        }
+
+        add_length_varint(tag, sizeof(T) * pbf_length_type(std::distance(first, last)));
+
+        while (first != last) {
+            add_fixed<T>(*first++);
+        }
+    }
+
+    template <typename It>
+    inline void add_packed_varint(pbf_tag_type tag, It first, It last) {
+        if (first == last) {
+            return;
+        }
+
+        pbf_writer sw(*this, tag);
+
+        while (first != last) {
+            sw.add_varint(uint64_t(*first++));
+        }
+    }
+
+    template <typename It>
+    inline void add_packed_svarint(pbf_tag_type tag, It first, It last) {
+        if (first == last) {
+            return;
+        }
+
+        pbf_writer sw(*this, tag);
+
+        while (first != last) {
+            sw.add_varint(encode_zigzag64(*first++));
+        }
+    }
+
+    // The number of bytes to reserve for the varint holding the length of
+    // a length-delimited field. The length has to fit into pbf_length_type,
+    // and a varint needs 8 bit for every 7 bit.
+    static const int reserve_bytes = sizeof(pbf_length_type) * 8 / 7 + 1;
+
+    inline void open_submessage(pbf_tag_type tag) {
+        pbf_assert(m_pos == 0);
+        pbf_assert(m_data);
+        add_field(tag, pbf_wire_type::length_delimited);
+        m_data->append(size_t(reserve_bytes), '\0');
+        m_pos = m_data->size();
+    }
+
+    inline void close_submessage() {
+        pbf_assert(m_pos != 0);
+        pbf_assert(m_data);
+        auto length = pbf_length_type(m_data->size() - m_pos);
+
+        pbf_assert(m_data->size() >= m_pos - reserve_bytes);
+        auto n = write_varint(m_data->begin() + long(m_pos) - reserve_bytes, length);
+
+        m_data->erase(m_data->begin() + long(m_pos) - reserve_bytes + n, m_data->begin() + long(m_pos));
+        m_pos = 0;
+    }
+
+    inline void add_length_varint(pbf_tag_type tag, pbf_length_type length) {
+        add_field(tag, pbf_wire_type::length_delimited);
+        add_varint(length);
+    }
+
+public:
+
+    /**
+     * Create a writer using the given string as a data store. The pbf_writer
+     * stores a reference to that string and adds all data to it.
+     */
+    inline explicit pbf_writer(std::string& data) noexcept :
+        m_data(&data),
+        m_parent_writer(nullptr),
+        m_pos(0) {
+    }
+
+    /**
+     * Create a writer without a data store. In this form the writer can not
+     * be used!
+     */
+    inline pbf_writer() noexcept :
+        m_data(nullptr),
+        m_parent_writer(nullptr),
+        m_pos(0) {
+    }
+
+    /**
+     * Construct a pbf_writer for a submessage from the pbf_writer of the
+     * parent message.
+     *
+     * @param parent_writer The pbf_writer
+     * @param tag Tag (field number) of the field that will be written
+     */
+    inline pbf_writer(pbf_writer& parent_writer, pbf_tag_type tag) :
+        m_data(parent_writer.m_data),
+        m_parent_writer(&parent_writer),
+        m_pos(0) {
+        m_parent_writer->open_submessage(tag);
+    }
+
+    /// A pbf_writer object can be copied
+    pbf_writer(const pbf_writer&) noexcept = default;
+
+    /// A pbf_writer object can be copied
+    pbf_writer& operator=(const pbf_writer&) noexcept = default;
+
+    /// A pbf_writer object can be moved
+    inline pbf_writer(pbf_writer&&) noexcept = default;
+
+    /// A pbf_writer object can be moved
+    inline pbf_writer& operator=(pbf_writer&&) noexcept = default;
+
+    inline ~pbf_writer() {
+        if (m_parent_writer) {
+            m_parent_writer->close_submessage();
+        }
+    }
+
+    ///@{
+    /**
+     * @name Scalar field writer functions
+     */
+
+    /**
+     * Add "bool" field to data.
+     *
+     * @param tag Tag (field number) of the field
+     * @param value Value to be written
+     */
+    inline void add_bool(pbf_tag_type tag, bool value) {
+        add_field(tag, pbf_wire_type::varint);
+        add_fixed<char>(value);
+    }
+
+    /**
+     * Add "enum" field to data.
+     *
+     * @param tag Tag (field number) of the field
+     * @param value Value to be written
+     */
+    inline void add_enum(pbf_tag_type tag, int32_t value) {
+        add_tagged_varint(tag, uint64_t(value));
+    }
+
+    /**
+     * Add "int32" field to data.
+     *
+     * @param tag Tag (field number) of the field
+     * @param value Value to be written
+     */
+    inline void add_int32(pbf_tag_type tag, int32_t value) {
+        add_tagged_varint(tag, uint64_t(value));
+    }
+
+    /**
+     * Add "sint32" field to data.
+     *
+     * @param tag Tag (field number) of the field
+     * @param value Value to be written
+     */
+    inline void add_sint32(pbf_tag_type tag, int32_t value) {
+        add_tagged_varint(tag, encode_zigzag32(value));
+    }
+
+    /**
+     * Add "uint32" field to data.
+     *
+     * @param tag Tag (field number) of the field
+     * @param value Value to be written
+     */
+    inline void add_uint32(pbf_tag_type tag, uint32_t value) {
+        add_tagged_varint(tag, value);
+    }
+
+    /**
+     * Add "int64" field to data.
+     *
+     * @param tag Tag (field number) of the field
+     * @param value Value to be written
+     */
+    inline void add_int64(pbf_tag_type tag, int64_t value) {
+        add_tagged_varint(tag, uint64_t(value));
+    }
+
+    /**
+     * Add "sint64" field to data.
+     *
+     * @param tag Tag (field number) of the field
+     * @param value Value to be written
+     */
+    inline void add_sint64(pbf_tag_type tag, int64_t value) {
+        add_tagged_varint(tag, encode_zigzag64(value));
+    }
+
+    /**
+     * Add "uint64" field to data.
+     *
+     * @param tag Tag (field number) of the field
+     * @param value Value to be written
+     */
+    inline void add_uint64(pbf_tag_type tag, uint64_t value) {
+        add_tagged_varint(tag, value);
+    }
+
+    /**
+     * Add "fixed32" field to data.
+     *
+     * @param tag Tag (field number) of the field
+     * @param value Value to be written
+     */
+    inline void add_fixed32(pbf_tag_type tag, uint32_t value) {
+        add_field(tag, pbf_wire_type::fixed32);
+        add_fixed<uint32_t>(value);
+    }
+
+    /**
+     * Add "sfixed32" field to data.
+     *
+     * @param tag Tag (field number) of the field
+     * @param value Value to be written
+     */
+    inline void add_sfixed32(pbf_tag_type tag, int32_t value) {
+        add_field(tag, pbf_wire_type::fixed32);
+        add_fixed<int32_t>(value);
+    }
+
+    /**
+     * Add "fixed64" field to data.
+     *
+     * @param tag Tag (field number) of the field
+     * @param value Value to be written
+     */
+    inline void add_fixed64(pbf_tag_type tag, uint64_t value) {
+        add_field(tag, pbf_wire_type::fixed64);
+        add_fixed<uint64_t>(value);
+    }
+
+    /**
+     * Add "sfixed64" field to data.
+     *
+     * @param tag Tag (field number) of the field
+     * @param value Value to be written
+     */
+    inline void add_sfixed64(pbf_tag_type tag, int64_t value) {
+        add_field(tag, pbf_wire_type::fixed64);
+        add_fixed<int64_t>(value);
+    }
+
+    /**
+     * Add "float" field to data.
+     *
+     * @param tag Tag (field number) of the field
+     * @param value Value to be written
+     */
+    inline void add_float(pbf_tag_type tag, float value) {
+        add_field(tag, pbf_wire_type::fixed32);
+        add_fixed<float>(value);
+    }
+
+    /**
+     * Add "double" field to data.
+     *
+     * @param tag Tag (field number) of the field
+     * @param value Value to be written
+     */
+    inline void add_double(pbf_tag_type tag, double value) {
+        add_field(tag, pbf_wire_type::fixed64);
+        add_fixed<double>(value);
+    }
+
+    /**
+     * Add "bytes" field to data.
+     *
+     * @param tag Tag (field number) of the field
+     * @param value Pointer to value to be written
+     * @param size Number of bytes to be written
+     */
+    inline void add_bytes(pbf_tag_type tag, const char* value, size_t size) {
+        pbf_assert(m_pos == 0 && "you can't add fields to a parent pbf_writer if there is an existing pbf_writer for a submessage");
+        pbf_assert(m_data);
+        assert(size <= std::numeric_limits<pbf_length_type>::max());
+        add_length_varint(tag, pbf_length_type(size));
+        m_data->append(value, size);
+    }
+
+    /**
+     * Add "bytes" field to data.
+     *
+     * @param tag Tag (field number) of the field
+     * @param value Value to be written
+     */
+    inline void add_bytes(pbf_tag_type tag, const std::string& value) {
+        add_bytes(tag, value.data(), value.size());
+    }
+
+    /**
+     * Add "string" field to data.
+     *
+     * @param tag Tag (field number) of the field
+     * @param value Pointer to value to be written
+     * @param size Number of bytes to be written
+     */
+    inline void add_string(pbf_tag_type tag, const char* value, size_t size) {
+        add_bytes(tag, value, size);
+    }
+
+    /**
+     * Add "string" field to data.
+     *
+     * @param tag Tag (field number) of the field
+     * @param value Value to be written
+     */
+    inline void add_string(pbf_tag_type tag, const std::string& value) {
+        add_bytes(tag, value.data(), value.size());
+    }
+
+    /**
+     * Add "string" field to data. Bytes from the value are written until
+     * a null byte is encountered. The null byte is not added.
+     *
+     * @param tag Tag (field number) of the field
+     * @param value Pointer to value to be written
+     */
+    inline void add_string(pbf_tag_type tag, const char* value) {
+        add_bytes(tag, value, std::strlen(value));
+    }
+
+    /**
+     * Add "message" field to data.
+     *
+     * @param tag Tag (field number) of the field
+     * @param value Pointer to message to be written
+     * @param size Length of the message
+     */
+    inline void add_message(pbf_tag_type tag, const char* value, size_t size) {
+        add_bytes(tag, value, size);
+    }
+
+    /**
+     * Add "message" field to data.
+     *
+     * @param tag Tag (field number) of the field
+     * @param value Value to be written. The value must be a complete message.
+     */
+    inline void add_message(pbf_tag_type tag, const std::string& value) {
+        add_bytes(tag, value.data(), value.size());
+    }
+
+    ///@}
+
+    ///@{
+    /**
+     * @name Repeated packed field writer functions
+     */
+
+    /**
+     * Add "repeated packed bool" field to data.
+     *
+     * @tparam InputIterator An type satisfying the InputIterator concept.
+     *         Dereferencing the iterator must yield a type assignable to bool.
+     * @param tag Tag (field number) of the field
+     * @param first Iterator pointing to the beginning of the data
+     * @param last Iterator pointing one past the end of data
+     */
+    template <typename InputIterator>
+    inline void add_packed_bool(pbf_tag_type tag, InputIterator first, InputIterator last) {
+        add_packed_varint(tag, first, last);
+    }
+
+    /**
+     * Add "repeated packed enum" field to data.
+     *
+     * @tparam InputIterator An type satisfying the InputIterator concept.
+     *         Dereferencing the iterator must yield a type assignable to int32_t.
+     * @param tag Tag (field number) of the field
+     * @param first Iterator pointing to the beginning of the data
+     * @param last Iterator pointing one past the end of data
+     */
+    template <typename InputIterator>
+    inline void add_packed_enum(pbf_tag_type tag, InputIterator first, InputIterator last) {
+        add_packed_varint(tag, first, last);
+    }
+
+    /**
+     * Add "repeated packed int32" field to data.
+     *
+     * @tparam InputIterator An type satisfying the InputIterator concept.
+     *         Dereferencing the iterator must yield a type assignable to int32_t.
+     * @param tag Tag (field number) of the field
+     * @param first Iterator pointing to the beginning of the data
+     * @param last Iterator pointing one past the end of data
+     */
+    template <typename InputIterator>
+    inline void add_packed_int32(pbf_tag_type tag, InputIterator first, InputIterator last) {
+        add_packed_varint(tag, first, last);
+    }
+
+    /**
+     * Add "repeated packed sint32" field to data.
+     *
+     * @tparam InputIterator An type satisfying the InputIterator concept.
+     *         Dereferencing the iterator must yield a type assignable to int32_t.
+     * @param tag Tag (field number) of the field
+     * @param first Iterator pointing to the beginning of the data
+     * @param last Iterator pointing one past the end of data
+     */
+    template <typename InputIterator>
+    inline void add_packed_sint32(pbf_tag_type tag, InputIterator first, InputIterator last) {
+        add_packed_svarint(tag, first, last);
+    }
+
+    /**
+     * Add "repeated packed uint32" field to data.
+     *
+     * @tparam InputIterator An type satisfying the InputIterator concept.
+     *         Dereferencing the iterator must yield a type assignable to uint32_t.
+     * @param tag Tag (field number) of the field
+     * @param first Iterator pointing to the beginning of the data
+     * @param last Iterator pointing one past the end of data
+     */
+    template <typename InputIterator>
+    inline void add_packed_uint32(pbf_tag_type tag, InputIterator first, InputIterator last) {
+        add_packed_varint(tag, first, last);
+    }
+
+    /**
+     * Add "repeated packed int64" field to data.
+     *
+     * @tparam InputIterator An type satisfying the InputIterator concept.
+     *         Dereferencing the iterator must yield a type assignable to int64_t.
+     * @param tag Tag (field number) of the field
+     * @param first Iterator pointing to the beginning of the data
+     * @param last Iterator pointing one past the end of data
+     */
+    template <typename InputIterator>
+    inline void add_packed_int64(pbf_tag_type tag, InputIterator first, InputIterator last) {
+        add_packed_varint(tag, first, last);
+    }
+
+    /**
+     * Add "repeated packed sint64" field to data.
+     *
+     * @tparam InputIterator An type satisfying the InputIterator concept.
+     *         Dereferencing the iterator must yield a type assignable to int64_t.
+     * @param tag Tag (field number) of the field
+     * @param first Iterator pointing to the beginning of the data
+     * @param last Iterator pointing one past the end of data
+     */
+    template <typename InputIterator>
+    inline void add_packed_sint64(pbf_tag_type tag, InputIterator first, InputIterator last) {
+        add_packed_svarint(tag, first, last);
+    }
+
+    /**
+     * Add "repeated packed uint64" field to data.
+     *
+     * @tparam InputIterator An type satisfying the InputIterator concept.
+     *         Dereferencing the iterator must yield a type assignable to uint64_t.
+     * @param tag Tag (field number) of the field
+     * @param first Iterator pointing to the beginning of the data
+     * @param last Iterator pointing one past the end of data
+     */
+    template <typename InputIterator>
+    inline void add_packed_uint64(pbf_tag_type tag, InputIterator first, InputIterator last) {
+        add_packed_varint(tag, first, last);
+    }
+
+    /**
+     * Add "repeated packed fixed32" field to data.
+     *
+     * @tparam InputIterator An type satisfying the InputIterator concept.
+     *         Dereferencing the iterator must yield a type assignable to uint32_t.
+     * @param tag Tag (field number) of the field
+     * @param first Iterator pointing to the beginning of the data
+     * @param last Iterator pointing one past the end of data
+     */
+    template <typename InputIterator>
+    inline void add_packed_fixed32(pbf_tag_type tag, InputIterator first, InputIterator last) {
+        add_packed_fixed<uint32_t, InputIterator>(tag, first, last,
+            typename std::iterator_traits<InputIterator>::iterator_category());
+    }
+
+    /**
+     * Add "repeated packed sfixed32" field to data.
+     *
+     * @tparam InputIterator An type satisfying the InputIterator concept.
+     *         Dereferencing the iterator must yield a type assignable to int32_t.
+     * @param tag Tag (field number) of the field
+     * @param first Iterator pointing to the beginning of the data
+     * @param last Iterator pointing one past the end of data
+     */
+    template <typename InputIterator>
+    inline void add_packed_sfixed32(pbf_tag_type tag, InputIterator first, InputIterator last) {
+        add_packed_fixed<int32_t, InputIterator>(tag, first, last,
+            typename std::iterator_traits<InputIterator>::iterator_category());
+    }
+
+    /**
+     * Add "repeated packed fixed64" field to data.
+     *
+     * @tparam InputIterator An type satisfying the InputIterator concept.
+     *         Dereferencing the iterator must yield a type assignable to uint64_t.
+     * @param tag Tag (field number) of the field
+     * @param first Iterator pointing to the beginning of the data
+     * @param last Iterator pointing one past the end of data
+     */
+    template <typename InputIterator>
+    inline void add_packed_fixed64(pbf_tag_type tag, InputIterator first, InputIterator last) {
+        add_packed_fixed<uint64_t, InputIterator>(tag, first, last,
+            typename std::iterator_traits<InputIterator>::iterator_category());
+    }
+
+    /**
+     * Add "repeated packed sfixed64" field to data.
+     *
+     * @tparam InputIterator An type satisfying the InputIterator concept.
+     *         Dereferencing the iterator must yield a type assignable to int64_t.
+     * @param tag Tag (field number) of the field
+     * @param first Iterator pointing to the beginning of the data
+     * @param last Iterator pointing one past the end of data
+     */
+    template <typename InputIterator>
+    inline void add_packed_sfixed64(pbf_tag_type tag, InputIterator first, InputIterator last) {
+        add_packed_fixed<int64_t, InputIterator>(tag, first, last,
+            typename std::iterator_traits<InputIterator>::iterator_category());
+    }
+
+    /**
+     * Add "repeated packed float" field to data.
+     *
+     * @tparam InputIterator An type satisfying the InputIterator concept.
+     *         Dereferencing the iterator must yield a type assignable to float.
+     * @param tag Tag (field number) of the field
+     * @param first Iterator pointing to the beginning of the data
+     * @param last Iterator pointing one past the end of data
+     */
+    template <typename InputIterator>
+    inline void add_packed_float(pbf_tag_type tag, InputIterator first, InputIterator last) {
+        add_packed_fixed<float, InputIterator>(tag, first, last,
+            typename std::iterator_traits<InputIterator>::iterator_category());
+    }
+
+    /**
+     * Add "repeated packed double" field to data.
+     *
+     * @tparam InputIterator An type satisfying the InputIterator concept.
+     *         Dereferencing the iterator must yield a type assignable to double.
+     * @param tag Tag (field number) of the field
+     * @param first Iterator pointing to the beginning of the data
+     * @param last Iterator pointing one past the end of data
+     */
+    template <typename InputIterator>
+    inline void add_packed_double(pbf_tag_type tag, InputIterator first, InputIterator last) {
+        add_packed_fixed<double, InputIterator>(tag, first, last,
+            typename std::iterator_traits<InputIterator>::iterator_category());
+    }
+
+    ///@}
+
+}; // class pbf_writer
+
+} // end namespace protozero
+
+#endif // PROTOZERO_PBF_WRITER_HPP
diff --git a/include/protozero/varint.hpp b/include/protozero/varint.hpp
new file mode 100644
index 0000000..bc9c329
--- /dev/null
+++ b/include/protozero/varint.hpp
@@ -0,0 +1,136 @@
+#ifndef PROTOZERO_VARINT_HPP
+#define PROTOZERO_VARINT_HPP
+
+/*****************************************************************************
+
+protozero - Minimalistic protocol buffer decoder and encoder in C++.
+
+This file is from https://github.com/mapbox/protozero where you can find more
+documentation.
+
+*****************************************************************************/
+
+/**
+ * @file varint.hpp
+ *
+ * @brief Contains low-level varint and zigzag encoding and decoding functions.
+ */
+
+#if __BYTE_ORDER != __LITTLE_ENDIAN
+# error "This code only works on little endian machines."
+#endif
+
+#include <cstdint>
+
+#include <protozero/exception.hpp>
+
+namespace protozero {
+
+/**
+ * The maximum length of a 64bit varint.
+ */
+const int8_t max_varint_length = sizeof(uint64_t) * 8 / 7 + 1;
+
+// from https://github.com/facebook/folly/blob/master/folly/Varint.h
+/**
+ * Decode a 64bit varint.
+ *
+ * String exception guarantee: if there is an exception the data pointer will
+ * not be changed.
+ *
+ * @param[in,out] data Pointer to pointer to the input data. After the function
+ *        returns this will point to the next data to be read.
+ * @param[in] end Pointer one past the end of the input data.
+ * @returns The decoded integer
+ * @throws varint_too_long_exception if the varint is longer then the maximum
+ *         length that would fit in a 64bit int. Usually this means your data
+ *         is corrupted or you are trying to read something as a varint that
+ *         isn't.
+ * @throws end_of_buffer_exception if the *end* of the buffer was reached
+ *         before the end of the varint.
+ */
+inline uint64_t decode_varint(const char** data, const char* end) {
+    const int8_t* begin = reinterpret_cast<const int8_t*>(*data);
+    const int8_t* iend = reinterpret_cast<const int8_t*>(end);
+    const int8_t* p = begin;
+    uint64_t val = 0;
+
+    if (iend - begin >= max_varint_length) {  // fast path
+        do {
+            int64_t b;
+            b = *p++; val  = uint64_t((b & 0x7f)      ); if (b >= 0) break;
+            b = *p++; val |= uint64_t((b & 0x7f) <<  7); if (b >= 0) break;
+            b = *p++; val |= uint64_t((b & 0x7f) << 14); if (b >= 0) break;
+            b = *p++; val |= uint64_t((b & 0x7f) << 21); if (b >= 0) break;
+            b = *p++; val |= uint64_t((b & 0x7f) << 28); if (b >= 0) break;
+            b = *p++; val |= uint64_t((b & 0x7f) << 35); if (b >= 0) break;
+            b = *p++; val |= uint64_t((b & 0x7f) << 42); if (b >= 0) break;
+            b = *p++; val |= uint64_t((b & 0x7f) << 49); if (b >= 0) break;
+            b = *p++; val |= uint64_t((b & 0x7f) << 56); if (b >= 0) break;
+            b = *p++; val |= uint64_t((b & 0x7f) << 63); if (b >= 0) break;
+            throw varint_too_long_exception();
+        } while (false);
+    } else {
+        int shift = 0;
+        while (p != iend && *p < 0) {
+            val |= uint64_t(*p++ & 0x7f) << shift;
+            shift += 7;
+        }
+        if (p == iend) {
+            throw end_of_buffer_exception();
+        }
+        val |= uint64_t(*p++) << shift;
+    }
+
+    *data = reinterpret_cast<const char*>(p);
+    return val;
+}
+
+/**
+ * Varint-encode a 64bit integer.
+ */
+template <typename OutputIterator>
+inline int write_varint(OutputIterator data, uint64_t value) {
+    int n=1;
+
+    while (value >= 0x80) {
+        *data++ = char((value & 0x7f) | 0x80);
+        value >>= 7;
+        ++n;
+    }
+    *data++ = char(value);
+
+    return n;
+}
+
+/**
+ * ZigZag encodes a 32 bit integer.
+ */
+inline uint32_t encode_zigzag32(int32_t value) noexcept {
+    return (static_cast<uint32_t>(value) << 1) ^ (static_cast<uint32_t>(value >> 31));
+}
+
+/**
+ * ZigZag encodes a 64 bit integer.
+ */
+inline uint64_t encode_zigzag64(int64_t value) noexcept {
+    return (static_cast<uint64_t>(value) << 1) ^ (static_cast<uint64_t>(value >> 63));
+}
+
+/**
+ * Decodes a 32 bit ZigZag-encoded integer.
+ */
+inline int32_t decode_zigzag32(uint32_t value) noexcept {
+    return int32_t(value >> 1) ^ -int32_t(value & 1);
+}
+
+/**
+ * Decodes a 64 bit ZigZag-encoded integer.
+ */
+inline int64_t decode_zigzag64(uint64_t value) noexcept {
+    return int64_t(value >> 1) ^ -int64_t(value & 1);
+}
+
+} // end namespace protozero
+
+#endif // PROTOZERO_VARINT_HPP
diff --git a/protozero.gyp b/protozero.gyp
new file mode 100644
index 0000000..2fa8d0a
--- /dev/null
+++ b/protozero.gyp
@@ -0,0 +1,52 @@
+{
+  "includes": [
+      "common.gypi"
+  ],
+  "targets": [
+    {
+      "target_name": "tests",
+      "type": "executable",
+      "sources": [
+        "test/tests.cpp",
+        "test/t/basic/test_cases.cpp",
+        "test/t/bool/test_cases.cpp",
+        "test/t/bytes/test_cases.cpp",
+        "test/t/complex/test_cases.cpp",
+        "test/t/double/test_cases.cpp",
+        "test/t/enum/test_cases.cpp",
+        "test/t/fixed32/test_cases.cpp",
+        "test/t/fixed64/test_cases.cpp",
+        "test/t/float/test_cases.cpp",
+        "test/t/int32/test_cases.cpp",
+        "test/t/int64/test_cases.cpp",
+        "test/t/message/test_cases.cpp",
+        "test/t/repeated/test_cases.cpp",
+        "test/t/repeated_packed_fixed32/test_cases.cpp",
+        "test/t/repeated_packed_fixed64/test_cases.cpp",
+        "test/t/repeated_packed_int32/test_cases.cpp",
+        "test/t/repeated_packed_int64/test_cases.cpp",
+        "test/t/repeated_packed_sfixed32/test_cases.cpp",
+        "test/t/repeated_packed_sfixed64/test_cases.cpp",
+        "test/t/repeated_packed_sint32/test_cases.cpp",
+        "test/t/repeated_packed_sint64/test_cases.cpp",
+        "test/t/repeated_packed_uint32/test_cases.cpp",
+        "test/t/repeated_packed_uint64/test_cases.cpp",
+        "test/t/sfixed32/test_cases.cpp",
+        "test/t/sfixed64/test_cases.cpp",
+        "test/t/sint32/test_cases.cpp",
+        "test/t/sint64/test_cases.cpp",
+        "test/t/skip/test_cases.cpp",
+        "test/t/string/test_cases.cpp",
+        "test/t/tags/test_cases.cpp",
+        "test/t/uint32/test_cases.cpp",
+        "test/t/uint64/test_cases.cpp",
+        "test/t/vector_tile/test_cases.cpp",
+        "test/t/zigzag/test_cases.cpp"
+      ],
+      "include_dirs": [
+          "./include/",
+          "./test/include/"
+      ]
+    }
+  ]
+}
diff --git a/test/.gitignore b/test/.gitignore
new file mode 100644
index 0000000..02a9cc0
--- /dev/null
+++ b/test/.gitignore
@@ -0,0 +1,6 @@
+tests.o
+tests
+writer_tests.o
+writer_tests
+*.gcda
+*.gcno
diff --git a/test/README.md b/test/README.md
new file mode 100644
index 0000000..fa97a7f
--- /dev/null
+++ b/test/README.md
@@ -0,0 +1,46 @@
+
+# Tests
+
+Tests are using the [Catch Unit Test Framework](https://github.com/philsquared/Catch).
+
+Call `make test` to compile and run all tests.
+
+
+## Organization of the test cases
+
+Each test case is in its own directory under the `t` directory. Each directory
+contains (some of) the following files:
+
+* `test_cases.cpp`: The C++ source code that runs the tests.
+* `writer_test_cases.cpp`: The C++ source code that runs extra writer tests.
+* `data-*.pbf`: PBF data files used by the tests.
+* `testcase.proto`: Protobuf file describing the format of the data files.
+* `testcase.cpp`: C++ file for creating the data files.
+
+### Read/write tests
+
+The `Makefile` automatically finds all the `test_cases.cpp` files and
+compiles them. Together with the `tests.cpp` file they make up the
+`tests` executable which can be called to execute all the read tests.
+
+### Extra writer tests
+
+The `Makefile` automatically finds all the `writer_test_cases.cpp` files and
+compiles them. Together with the `writer_tests.cpp` file they make up the
+`writer_tests` executable which can be called to execute all the write tests.
+
+The extra writer tests need the Google protobuf library to work.
+
+
+## Creating test data from scratch
+
+Most tests use test data stored in PBF format in their directory. The files
+have the suffix `.pbf`. Most of those files have been generated from the
+provided `testcase.proto` and `testcase.cpp` files.
+
+Usually you do not have to do this, but if you want to re-generate the PBF
+data files, you can do so:
+
+    cd test
+    ./create_pbf_test_data.sh
+
diff --git a/test/create_pbf_test_data.sh b/test/create_pbf_test_data.sh
new file mode 100755
index 0000000..2e25c31
--- /dev/null
+++ b/test/create_pbf_test_data.sh
@@ -0,0 +1,28 @@
+#!/bin/sh
+#
+#  create_pbf_test_data.sh [TESTCASE]
+#
+#  This script creates the test data for the given test case in protobuf format
+#  using the testcase.proto description and the testcase.cpp code.
+#
+#  If called without a test case it will iterate over all test cases generating
+#  all data.
+#
+
+set -e
+
+if [ -z "$1" ]; then
+    for dir in t/*; do
+        $0 $dir
+    done
+fi
+
+echo "Generating $1..."
+cd $1
+if [ -f testcase.proto ]; then
+    protoc --cpp_out=. testcase.proto
+    $CXX -std=c++11 -I../../include -o testcase testcase.cpp testcase.pb.cc -lprotobuf-lite -pthreads
+    ./testcase
+fi
+cd ../..
+
diff --git a/test/include/catch.hpp b/test/include/catch.hpp
new file mode 100644
index 0000000..de61226
--- /dev/null
+++ b/test/include/catch.hpp
@@ -0,0 +1,9416 @@
+/*
+ *  Catch v1.2.1
+ *  Generated: 2015-06-30 18:23:27.961086
+ *  ----------------------------------------------------------
+ *  This file has been merged from multiple headers. Please don't edit it directly
+ *  Copyright (c) 2012 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
+
+#define TWOBLUECUBES_CATCH_HPP_INCLUDED
+
+#ifdef __clang__
+#    pragma clang system_header
+#elif defined __GNUC__
+#    pragma GCC system_header
+#endif
+
+// #included from: internal/catch_suppress_warnings.h
+
+#define TWOBLUECUBES_CATCH_SUPPRESS_WARNINGS_H_INCLUDED
+
+#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"
+#    endif
+#elif defined __GNUC__
+#    pragma GCC diagnostic ignored "-Wvariadic-macros"
+#    pragma GCC diagnostic ignored "-Wunused-variable"
+#    pragma GCC diagnostic push
+#    pragma GCC diagnostic ignored "-Wpadded"
+#endif
+
+#if defined(CATCH_CONFIG_MAIN) || defined(CATCH_CONFIG_RUNNER)
+#  define CATCH_IMPL
+#endif
+
+#ifdef CATCH_IMPL
+#  ifndef CLARA_CONFIG_MAIN
+#    define CLARA_CONFIG_MAIN_NOT_DEFINED
+#    define CLARA_CONFIG_MAIN
+#  endif
+#endif
+
+// #included from: internal/catch_notimplemented_exception.h
+#define TWOBLUECUBES_CATCH_NOTIMPLEMENTED_EXCEPTION_H_INCLUDED
+
+// #included from: catch_common.h
+#define TWOBLUECUBES_CATCH_COMMON_H_INCLUDED
+
+#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 )
+#define INTERNAL_CATCH_UNIQUE_NAME( name ) INTERNAL_CATCH_UNIQUE_NAME_LINE( name, __LINE__ )
+
+#define INTERNAL_CATCH_STRINGIFY2( expr ) #expr
+#define INTERNAL_CATCH_STRINGIFY( expr ) INTERNAL_CATCH_STRINGIFY2( expr )
+
+#include <sstream>
+#include <stdexcept>
+#include <algorithm>
+
+// #included from: catch_compiler_capabilities.h
+#define TWOBLUECUBES_CATCH_COMPILER_CAPABILITIES_HPP_INCLUDED
+
+// Detect a number of compiler features - mostly C++11/14 conformance - 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_OR_GREATER : Is C++11 supported?
+
+// CATCH_CONFIG_VARIADIC_MACROS : are variadic macros supported?
+
+// In general each macro has a _NO_<feature name> form
+// (e.g. CATCH_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.
+
+// All the C++11 features can be disabled with CATCH_CONFIG_NO_CPP11
+
+#ifdef __clang__
+
+#  if __has_feature(cxx_nullptr)
+#    define CATCH_INTERNAL_CONFIG_CPP11_NULLPTR
+#  endif
+
+#  if __has_feature(cxx_noexcept)
+#    define CATCH_INTERNAL_CONFIG_CPP11_NOEXCEPT
+#  endif
+
+#endif // __clang__
+
+////////////////////////////////////////////////////////////////////////////////
+// Borland
+#ifdef __BORLANDC__
+
+#endif // __BORLANDC__
+
+////////////////////////////////////////////////////////////////////////////////
+// EDG
+#ifdef __EDG_VERSION__
+
+#endif // __EDG_VERSION__
+
+////////////////////////////////////////////////////////////////////////////////
+// Digital Mars
+#ifdef __DMC__
+
+#endif // __DMC__
+
+////////////////////////////////////////////////////////////////////////////////
+// GCC
+#ifdef __GNUC__
+
+#if __GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 6 && defined(__GXX_EXPERIMENTAL_CXX0X__) )
+#   define CATCH_INTERNAL_CONFIG_CPP11_NULLPTR
+#endif
+
+#endif // __GNUC__
+
+////////////////////////////////////////////////////////////////////////////////
+// Visual C++
+#ifdef _MSC_VER
+
+#if (_MSC_VER >= 1600)
+#   define CATCH_INTERNAL_CONFIG_CPP11_NULLPTR
+#endif
+
+#if (_MSC_VER >= 1900 ) // (VC++ 13 (VS2015))
+#define CATCH_INTERNAL_CONFIG_CPP11_NOEXCEPT
+#define CATCH_INTERNAL_CONFIG_CPP11_GENERATED_METHODS
+#endif
+
+#endif // _MSC_VER
+
+// 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
+
+#endif
+
+////////////////////////////////////////////////////////////////////////////////
+// C++ language feature support
+
+// catch all support for C++11
+#if (__cplusplus >= 201103L)
+
+#  define CATCH_CPP11_OR_GREATER
+
+#  if !defined(CATCH_INTERNAL_CONFIG_CPP11_NULLPTR)
+#    define CATCH_INTERNAL_CONFIG_CPP11_NULLPTR
+#  endif
+
+#  ifndef CATCH_INTERNAL_CONFIG_CPP11_NOEXCEPT
+#    define CATCH_INTERNAL_CONFIG_CPP11_NOEXCEPT
+#  endif
+
+#  ifndef CATCH_INTERNAL_CONFIG_CPP11_GENERATED_METHODS
+#    define CATCH_INTERNAL_CONFIG_CPP11_GENERATED_METHODS
+#  endif
+
+#  ifndef CATCH_INTERNAL_CONFIG_CPP11_IS_ENUM
+#    define CATCH_INTERNAL_CONFIG_CPP11_IS_ENUM
+#  endif
+
+#  ifndef CATCH_INTERNAL_CONFIG_CPP11_TUPLE
+#    define CATCH_INTERNAL_CONFIG_CPP11_TUPLE
+#  endif
+
+#  ifndef CATCH_INTERNAL_CONFIG_VARIADIC_MACROS
+#    define CATCH_INTERNAL_CONFIG_VARIADIC_MACROS
+#  endif
+
+#endif // __cplusplus >= 201103L
+
+// 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
+#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
+#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
+#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
+#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
+#endif
+#if defined(CATCH_INTERNAL_CONFIG_VARIADIC_MACROS) && !defined(CATCH_CONFIG_NO_VARIADIC_MACROS) && !defined(CATCH_CONFIG_VARIADIC_MACROS)
+#define CATCH_CONFIG_VARIADIC_MACROS
+#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)
+#endif
+
+namespace Catch {
+
+    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() {}
+        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>
+    inline 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>
+    inline 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 endsWith( std::string const& s, std::string const& 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 );
+
+        std::size_t m_count;
+        std::string m_label;
+    };
+
+    struct SourceLineInfo {
+
+        SourceLineInfo();
+        SourceLineInfo( char const* _file, std::size_t _line );
+        SourceLineInfo( SourceLineInfo const& other );
+#  ifdef CATCH_CONFIG_CPP11_GENERATED_METHODS
+        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;
+
+        std::string file;
+        std::size_t line;
+    };
+
+    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 );
+
+    // Use this in variadic streaming macros to allow
+    //    >> +StreamEndStop
+    // as well as
+    //    >> stuff +StreamEndStop
+    struct StreamEndStop {
+        std::string operator+() {
+            return std::string();
+        }
+    };
+    template<typename T>
+    T const& operator + ( T const& value, StreamEndStop ) {
+        return value;
+    }
+}
+
+#define CATCH_INTERNAL_LINEINFO ::Catch::SourceLineInfo( __FILE__, static_cast<std::size_t>( __LINE__ ) )
+#define CATCH_INTERNAL_ERROR( msg ) ::Catch::throwLogicError( msg, CATCH_INTERNAL_LINEINFO );
+
+#include <ostream>
+
+namespace Catch {
+
+    class NotImplementedException : public std::exception
+    {
+    public:
+        NotImplementedException( SourceLineInfo const& lineInfo );
+        NotImplementedException( NotImplementedException const& ) {}
+
+        virtual ~NotImplementedException() CATCH_NOEXCEPT {}
+
+        virtual const char* what() const CATCH_NOEXCEPT;
+
+    private:
+        std::string m_what;
+        SourceLineInfo m_lineInfo;
+    };
+
+} // end namespace Catch
+
+///////////////////////////////////////////////////////////////////////////////
+#define CATCH_NOT_IMPLEMENTED throw Catch::NotImplementedException( CATCH_INTERNAL_LINEINFO )
+
+// #included from: internal/catch_context.h
+#define TWOBLUECUBES_CATCH_CONTEXT_H_INCLUDED
+
+// #included from: catch_interfaces_generators.h
+#define TWOBLUECUBES_CATCH_INTERFACES_GENERATORS_H_INCLUDED
+
+#include <string>
+
+namespace Catch {
+
+    struct IGeneratorInfo {
+        virtual ~IGeneratorInfo();
+        virtual bool moveNext() = 0;
+        virtual std::size_t getCurrentIndex() const = 0;
+    };
+
+    struct IGeneratorsForTest {
+        virtual ~IGeneratorsForTest();
+
+        virtual IGeneratorInfo& getGeneratorInfo( std::string const& fileInfo, std::size_t size ) = 0;
+        virtual bool moveNext() = 0;
+    };
+
+    IGeneratorsForTest* createGeneratorsForTest();
+
+} // end namespace Catch
+
+// #included from: catch_ptr.hpp
+#define TWOBLUECUBES_CATCH_PTR_HPP_INCLUDED
+
+#ifdef __clang__
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wpadded"
+#endif
+
+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 {
+    public:
+        Ptr() : m_p( 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 = 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() { return m_p; }
+        const T* get() const{ return m_p; }
+        T& operator*() const { return *m_p; }
+        T* operator->() const { return m_p; }
+        bool operator !() const { return m_p == NULL; }
+        operator SafeBool::type() const { return SafeBool::makeSafe( m_p != NULL ); }
+
+    private:
+        T* m_p;
+    };
+
+    struct IShared : NonCopyable {
+        virtual ~IShared();
+        virtual void addRef() const = 0;
+        virtual void release() const = 0;
+    };
+
+    template<typename T = IShared>
+    struct SharedImpl : T {
+
+        SharedImpl() : m_rc( 0 ){}
+
+        virtual void addRef() const {
+            ++m_rc;
+        }
+        virtual void release() const {
+            if( --m_rc == 0 )
+                delete this;
+        }
+
+        mutable unsigned int m_rc;
+    };
+
+} // end namespace Catch
+
+#ifdef __clang__
+#pragma clang diagnostic pop
+#endif
+
+#include <memory>
+#include <vector>
+#include <stdlib.h>
+
+namespace Catch {
+
+    class TestCase;
+    class Stream;
+    struct IResultCapture;
+    struct IRunner;
+    struct IGeneratorsForTest;
+    struct IConfig;
+
+    struct IContext
+    {
+        virtual ~IContext();
+
+        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;
+    };
+
+    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;
+    };
+
+    IContext& getCurrentContext();
+    IMutableContext& getCurrentMutableContext();
+    void cleanUpContext();
+    Stream createStream( std::string const& streamName );
+
+}
+
+// #included from: internal/catch_test_registry.hpp
+#define TWOBLUECUBES_CATCH_TEST_REGISTRY_HPP_INCLUDED
+
+// #included from: catch_interfaces_testcase.h
+#define TWOBLUECUBES_CATCH_INTERFACES_TESTCASE_H_INCLUDED
+
+#include <vector>
+
+namespace Catch {
+
+    class TestSpec;
+
+    struct ITestCase : IShared {
+        virtual void invoke () const = 0;
+    protected:
+        virtual ~ITestCase();
+    };
+
+    class TestCase;
+    struct IConfig;
+
+    struct ITestCaseRegistry {
+        virtual ~ITestCaseRegistry();
+        virtual std::vector<TestCase> const& getAllTests() const = 0;
+        virtual void getFilteredTests( TestSpec const& testSpec, IConfig const& config, std::vector<TestCase>& matchingTestCases, bool negated = false ) const = 0;
+
+    };
+}
+
+namespace Catch {
+
+template<typename C>
+class MethodTestCase : public SharedImpl<ITestCase> {
+
+public:
+    MethodTestCase( void (C::*method)() ) : m_method( method ) {}
+
+    virtual void invoke() const {
+        C obj;
+        (obj.*m_method)();
+    }
+
+private:
+    virtual ~MethodTestCase() {}
+
+    void (C::*m_method)();
+};
+
+typedef void(*TestFunction)();
+
+struct NameAndDesc {
+    NameAndDesc( const char* _name = "", const char* _description= "" )
+    : name( _name ), description( _description )
+    {}
+
+    const char* name;
+    const char* description;
+};
+
+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 ) {
+        registerTestCase(   new MethodTestCase<C>( method ),
+                            className,
+                            nameAndDesc,
+                            lineInfo );
+    }
+
+    void registerTestCase(  ITestCase* testCase,
+                            char const* className,
+                            NameAndDesc const& nameAndDesc,
+                            SourceLineInfo const& lineInfo );
+
+    ~AutoReg();
+
+private:
+    AutoReg( AutoReg const& );
+    void operator= ( AutoReg const& );
+};
+
+} // end namespace Catch
+
+#ifdef CATCH_CONFIG_VARIADIC_MACROS
+    ///////////////////////////////////////////////////////////////////////////////
+    #define INTERNAL_CATCH_TESTCASE( ... ) \
+        static void INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_S_T____ )(); \
+        namespace{ Catch::AutoReg INTERNAL_CATCH_UNIQUE_NAME( autoRegistrar )( &INTERNAL_CATCH_UNIQUE_NAME(  ____C_A_T_C_H____T_E_S_T____ ), CATCH_INTERNAL_LINEINFO, Catch::NameAndDesc( __VA_ARGS__ ) ); }\
+        static void INTERNAL_CATCH_UNIQUE_NAME(  ____C_A_T_C_H____T_E_S_T____ )()
+
+    ///////////////////////////////////////////////////////////////////////////////
+    #define INTERNAL_CATCH_METHOD_AS_TEST_CASE( QualifiedMethod, ... ) \
+        namespace{ Catch::AutoReg INTERNAL_CATCH_UNIQUE_NAME( autoRegistrar )( &QualifiedMethod, "&" #QualifiedMethod, Catch::NameAndDesc( __VA_ARGS__ ), CATCH_INTERNAL_LINEINFO ); }
+
+    ///////////////////////////////////////////////////////////////////////////////
+    #define INTERNAL_CATCH_TEST_CASE_METHOD( ClassName, ... )\
+        namespace{ \
+            struct INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_S_T____ ) : ClassName{ \
+                void test(); \
+            }; \
+            Catch::AutoReg INTERNAL_CATCH_UNIQUE_NAME( autoRegistrar ) ( &INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_S_T____ )::test, #ClassName, Catch::NameAndDesc( __VA_ARGS__ ), CATCH_INTERNAL_LINEINFO ); \
+        } \
+        void INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_S_T____ )::test()
+
+#else
+    ///////////////////////////////////////////////////////////////////////////////
+    #define INTERNAL_CATCH_TESTCASE( Name, Desc ) \
+        static void INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_S_T____ )(); \
+        namespace{ Catch::AutoReg INTERNAL_CATCH_UNIQUE_NAME( autoRegistrar )( &INTERNAL_CATCH_UNIQUE_NAME(  ____C_A_T_C_H____T_E_S_T____ ), CATCH_INTERNAL_LINEINFO, Catch::NameAndDesc( Name, Desc ) ); }\
+        static void INTERNAL_CATCH_UNIQUE_NAME(  ____C_A_T_C_H____T_E_S_T____ )()
+
+    ///////////////////////////////////////////////////////////////////////////////
+    #define INTERNAL_CATCH_METHOD_AS_TEST_CASE( QualifiedMethod, Name, Desc ) \
+        namespace{ Catch::AutoReg INTERNAL_CATCH_UNIQUE_NAME( autoRegistrar )( &QualifiedMethod, "&" #QualifiedMethod, Catch::NameAndDesc( Name, Desc ), CATCH_INTERNAL_LINEINFO ); }
+
+    ///////////////////////////////////////////////////////////////////////////////
+    #define INTERNAL_CATCH_TEST_CASE_METHOD( ClassName, TestName, Desc )\
+        namespace{ \
+            struct INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_S_T____ ) : ClassName{ \
+                void test(); \
+            }; \
+            Catch::AutoReg INTERNAL_CATCH_UNIQUE_NAME( autoRegistrar ) ( &INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_S_T____ )::test, #ClassName, Catch::NameAndDesc( TestName, Desc ), CATCH_INTERNAL_LINEINFO ); \
+        } \
+        void INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_S_T____ )::test()
+
+#endif
+
+// #included from: internal/catch_capture.hpp
+#define TWOBLUECUBES_CATCH_CAPTURE_HPP_INCLUDED
+
+// #included from: catch_result_builder.h
+#define TWOBLUECUBES_CATCH_RESULT_BUILDER_H_INCLUDED
+
+// #included from: catch_result_type.h
+#define TWOBLUECUBES_CATCH_RESULT_TYPE_H_INCLUDED
+
+namespace Catch {
+
+    // ResultWas::OfType enum
+    struct ResultWas { enum OfType {
+        Unknown = -1,
+        Ok = 0,
+        Info = 1,
+        Warning = 2,
+
+        FailureBit = 0x10,
+
+        ExpressionFailed = FailureBit | 1,
+        ExplicitFailure = FailureBit | 2,
+
+        Exception = 0x100 | FailureBit,
+
+        ThrewException = Exception | 1,
+        DidntThrowException = Exception | 2,
+
+        FatalErrorCondition = 0x200 | FailureBit
+
+    }; };
+
+    inline bool isOk( ResultWas::OfType resultType ) {
+        return ( resultType & ResultWas::FailureBit ) == 0;
+    }
+    inline bool isJustInfo( int flags ) {
+        return flags == ResultWas::Info;
+    }
+
+    // ResultDisposition::Flags enum
+    struct ResultDisposition { enum Flags {
+        Normal = 0x01,
+
+        ContinueOnFailure = 0x02,   // Failures fail test, but execution continues
+        FalseTest = 0x04,           // Prefix expression with !
+        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 ) );
+    }
+
+    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; }
+
+} // end namespace Catch
+
+// #included from: catch_assertionresult.h
+#define TWOBLUECUBES_CATCH_ASSERTIONRESULT_H_INCLUDED
+
+#include <string>
+
+namespace Catch {
+
+    struct AssertionInfo
+    {
+        AssertionInfo() {}
+        AssertionInfo(  std::string const& _macroName,
+                        SourceLineInfo const& _lineInfo,
+                        std::string const& _capturedExpression,
+                        ResultDisposition::Flags _resultDisposition );
+
+        std::string macroName;
+        SourceLineInfo lineInfo;
+        std::string capturedExpression;
+        ResultDisposition::Flags resultDisposition;
+    };
+
+    struct AssertionResultData
+    {
+        AssertionResultData() : resultType( ResultWas::Unknown ) {}
+
+        std::string reconstructedExpression;
+        std::string message;
+        ResultWas::OfType resultType;
+    };
+
+    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
+
+        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;
+
+    protected:
+        AssertionInfo m_info;
+        AssertionResultData m_resultData;
+    };
+
+} // end namespace Catch
+
+namespace Catch {
+
+    struct TestFailureException{};
+
+    template<typename T> class ExpressionLhs;
+
+    struct STATIC_ASSERT_Expression_Too_Complex_Please_Rewrite_As_Binary_Comparison;
+
+    struct CopyableStream {
+        CopyableStream() {}
+        CopyableStream( CopyableStream const& other ) {
+            oss << other.oss.str();
+        }
+        CopyableStream& operator=( CopyableStream const& other ) {
+            oss.str("");
+            oss << other.oss.str();
+            return *this;
+        }
+        std::ostringstream oss;
+    };
+
+    class ResultBuilder {
+    public:
+        ResultBuilder(  char const* macroName,
+                        SourceLineInfo const& lineInfo,
+                        char const* capturedExpression,
+                        ResultDisposition::Flags resultDisposition );
+
+        template<typename T>
+        ExpressionLhs<T const&> operator <= ( T const& operand );
+        ExpressionLhs<bool> operator <= ( bool value );
+
+        template<typename T>
+        ResultBuilder& operator << ( T const& value ) {
+            m_stream.oss << value;
+            return *this;
+        }
+
+        template<typename RhsT> STATIC_ASSERT_Expression_Too_Complex_Please_Rewrite_As_Binary_Comparison& operator && ( RhsT const& );
+        template<typename RhsT> STATIC_ASSERT_Expression_Too_Complex_Please_Rewrite_As_Binary_Comparison& operator || ( RhsT const& );
+
+        ResultBuilder& setResultType( ResultWas::OfType result );
+        ResultBuilder& setResultType( bool result );
+        ResultBuilder& setLhs( std::string const& lhs );
+        ResultBuilder& setRhs( std::string const& rhs );
+        ResultBuilder& setOp( std::string const& op );
+
+        void endExpression();
+
+        std::string reconstructExpression() const;
+        AssertionResult build() const;
+
+        void useActiveException( ResultDisposition::Flags resultDisposition = ResultDisposition::Normal );
+        void captureResult( ResultWas::OfType resultType );
+        void captureExpression();
+        void react();
+        bool shouldDebugBreak() const;
+        bool allowThrows() const;
+
+    private:
+        AssertionInfo m_assertionInfo;
+        AssertionResultData m_data;
+        struct ExprComponents {
+            ExprComponents() : testFalse( false ) {}
+            bool testFalse;
+            std::string lhs, rhs, op;
+        } m_exprComponents;
+        CopyableStream m_stream;
+
+        bool m_shouldDebugBreak;
+        bool m_shouldThrow;
+    };
+
+} // 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
+#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>
+    inline 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>
+    class Evaluator{};
+
+    template<typename T1, typename T2>
+    struct Evaluator<T1, T2, IsEqualTo> {
+        static bool evaluate( T1 const& lhs, T2 const& rhs) {
+            return opCast( lhs ) ==  opCast( rhs );
+        }
+    };
+    template<typename T1, typename T2>
+    struct Evaluator<T1, T2, IsNotEqualTo> {
+        static bool evaluate( T1 const& lhs, T2 const& rhs ) {
+            return opCast( lhs ) != opCast( rhs );
+        }
+    };
+    template<typename T1, typename T2>
+    struct Evaluator<T1, T2, IsLessThan> {
+        static bool evaluate( T1 const& lhs, T2 const& rhs ) {
+            return opCast( lhs ) < opCast( rhs );
+        }
+    };
+    template<typename T1, typename T2>
+    struct Evaluator<T1, T2, IsGreaterThan> {
+        static bool evaluate( T1 const& lhs, T2 const& rhs ) {
+            return opCast( lhs ) > opCast( rhs );
+        }
+    };
+    template<typename T1, typename T2>
+    struct Evaluator<T1, T2, IsGreaterThanOrEqualTo> {
+        static bool evaluate( T1 const& lhs, T2 const& rhs ) {
+            return opCast( lhs ) >= opCast( rhs );
+        }
+    };
+    template<typename T1, typename T2>
+    struct Evaluator<T1, T2, IsLessThanOrEqualTo> {
+        static bool evaluate( T1 const& lhs, T2 const& rhs ) {
+            return 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_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( NULL, rhs );
+    }
+    template<Operator Op, typename T> bool compare( T* lhs, std::nullptr_t ) {
+        return Evaluator<T*, T*, Op>::evaluate( lhs, NULL );
+    }
+#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>
+
+#ifdef __has_feature
+#define CATCH_ARC_ENABLED __has_feature(objc_arc)
+#else
+#define CATCH_ARC_ENABLED 0
+#endif
+
+void arcSafeRelease( NSObject* obj );
+id performOptionalSelector( id obj, SEL sel );
+
+#if !CATCH_ARC_ENABLED
+inline void arcSafeRelease( NSObject* obj ) {
+    [obj release];
+}
+inline id performOptionalSelector( id obj, SEL sel ) {
+    if( [obj respondsToSelector: sel] )
+        return [obj performSelector: sel];
+    return nil;
+}
+#define CATCH_UNSAFE_UNRETAINED
+#define CATCH_ARC_STRONG
+#else
+inline void arcSafeRelease( NSObject* ){}
+inline id performOptionalSelector( id obj, SEL sel ) {
+#ifdef __clang__
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Warc-performSelector-leaks"
+#endif
+    if( [obj respondsToSelector: sel] )
+        return [obj performSelector: sel];
+#ifdef __clang__
+#pragma clang diagnostic pop
+#endif
+    return nil;
+}
+#define CATCH_UNSAFE_UNRETAINED __unsafe_unretained
+#define CATCH_ARC_STRONG __strong
+#endif
+
+#endif
+
+#ifdef CATCH_CONFIG_CPP11_TUPLE
+#include <tuple>
+#endif
+
+#ifdef CATCH_CONFIG_CPP11_IS_ENUM
+#include <type_traits>
+#endif
+
+namespace Catch {
+
+// Why we're here.
+template<typename T>
+std::string toString( T const& value );
+
+// Built in overloads
+
+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 );
+
+#ifdef CATCH_CONFIG_CPP11_NULLPTR
+std::string toString( std::nullptr_t );
+#endif
+
+#ifdef __OBJC__
+    std::string toString( NSString const * const& nsstring );
+    std::string toString( NSString * CATCH_ARC_STRONG const& nsstring );
+    std::string toString( NSObject* const& nsObject );
+#endif
+
+namespace Detail {
+
+    extern std::string unprintableString;
+
+    struct BorgType {
+        template<typename T> BorgType( T const& );
+    };
+
+    struct TrueType { char sizer[1]; };
+    struct FalseType { char sizer[2]; };
+
+    TrueType& testStreamable( std::ostream& );
+    FalseType testStreamable( FalseType );
+
+    FalseType operator<<( std::ostream const&, BorgType const& );
+
+    template<typename T>
+    struct IsStreamInsertable {
+        static std::ostream &s;
+        static T  const&t;
+        enum { value = sizeof( testStreamable(s << t) ) == sizeof( TrueType ) };
+    };
+
+#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; }
+    };
+
+    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)
+                );
+        }
+    };
+#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 );
+        }
+#else
+        template<typename T>
+        static std::string convert( T const& ) { return unprintableString; }
+#endif
+    };
+
+    template<>
+    struct StringMakerBase<true> {
+        template<typename T>
+        static std::string convert( T const& _value ) {
+            std::ostringstream oss;
+            oss << _value;
+            return oss.str();
+        }
+    };
+
+    std::string rawMemoryToString( const void *object, std::size_t size );
+
+    template<typename T>
+    inline std::string rawMemoryToString( const T& object ) {
+      return rawMemoryToString( &object, sizeof(object) );
+    }
+
+} // end namespace Detail
+
+template<typename T>
+struct StringMaker :
+    Detail::StringMakerBase<Detail::IsStreamInsertable<T>::value> {};
+
+template<typename T>
+struct StringMaker<T*> {
+    template<typename U>
+    static std::string convert( U* p ) {
+        if( !p )
+            return INTERNAL_CATCH_STRINGIFY( NULL );
+        else
+            return Detail::rawMemoryToString( p );
+    }
+};
+
+template<typename R, typename C>
+struct StringMaker<R C::*> {
+    static std::string convert( R C::* p ) {
+        if( !p )
+            return INTERNAL_CATCH_STRINGIFY( NULL );
+        else
+            return Detail::rawMemoryToString( p );
+    }
+};
+
+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 Tuple,
+      std::size_t N
+      >
+  struct ElementPrinter<Tuple,N,false> {
+      static void print( const Tuple&, std::ostream& ) {}
+  };
+
+}
+
+template<typename ...Types>
+struct StringMaker<std::tuple<Types...>> {
+
+    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 {
+    template<typename T>
+    std::string makeString( T const& value ) {
+        return StringMaker<T>::convert( value );
+    }
+} // end namespace Detail
+
+/// \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 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();
+    }
+}
+
+} // end namespace Catch
+
+namespace Catch {
+
+// Wraps the LHS of an expression and captures the operator and RHS (if any) -
+// wrapping them all in a ResultBuilder object
+template<typename T>
+class ExpressionLhs {
+    ExpressionLhs& operator = ( ExpressionLhs const& );
+#  ifdef CATCH_CONFIG_CPP11_GENERATED_METHODS
+    ExpressionLhs& operator = ( ExpressionLhs && ) = delete;
+#  endif
+
+public:
+    ExpressionLhs( ResultBuilder& rb, T lhs ) : m_rb( rb ), m_lhs( lhs ) {}
+#  ifdef CATCH_CONFIG_CPP11_GENERATED_METHODS
+    ExpressionLhs( ExpressionLhs const& ) = default;
+    ExpressionLhs( ExpressionLhs && )     = default;
+#  endif
+
+    template<typename RhsT>
+    ResultBuilder& operator == ( RhsT const& rhs ) {
+        return captureExpression<Internal::IsEqualTo>( rhs );
+    }
+
+    template<typename RhsT>
+    ResultBuilder& operator != ( RhsT const& rhs ) {
+        return captureExpression<Internal::IsNotEqualTo>( rhs );
+    }
+
+    template<typename RhsT>
+    ResultBuilder& operator < ( RhsT const& rhs ) {
+        return captureExpression<Internal::IsLessThan>( rhs );
+    }
+
+    template<typename RhsT>
+    ResultBuilder& operator > ( RhsT const& rhs ) {
+        return captureExpression<Internal::IsGreaterThan>( rhs );
+    }
+
+    template<typename RhsT>
+    ResultBuilder& operator <= ( RhsT const& rhs ) {
+        return captureExpression<Internal::IsLessThanOrEqualTo>( rhs );
+    }
+
+    template<typename RhsT>
+    ResultBuilder& operator >= ( RhsT const& rhs ) {
+        return captureExpression<Internal::IsGreaterThanOrEqualTo>( rhs );
+    }
+
+    ResultBuilder& operator == ( bool rhs ) {
+        return captureExpression<Internal::IsEqualTo>( rhs );
+    }
+
+    ResultBuilder& operator != ( bool rhs ) {
+        return captureExpression<Internal::IsNotEqualTo>( rhs );
+    }
+
+    void endExpression() {
+        bool value = m_lhs ? true : false;
+        m_rb
+            .setLhs( Catch::toString( value ) )
+            .setResultType( value )
+            .endExpression();
+    }
+
+    // Only simple binary expressions are allowed on the LHS.
+    // If more complex compositions are required then place the sub expression in parentheses
+    template<typename RhsT> STATIC_ASSERT_Expression_Too_Complex_Please_Rewrite_As_Binary_Comparison& operator + ( RhsT const& );
+    template<typename RhsT> STATIC_ASSERT_Expression_Too_Complex_Please_Rewrite_As_Binary_Comparison& operator - ( RhsT const& );
+    template<typename RhsT> STATIC_ASSERT_Expression_Too_Complex_Please_Rewrite_As_Binary_Comparison& operator / ( RhsT const& );
+    template<typename RhsT> STATIC_ASSERT_Expression_Too_Complex_Please_Rewrite_As_Binary_Comparison& operator * ( RhsT const& );
+    template<typename RhsT> STATIC_ASSERT_Expression_Too_Complex_Please_Rewrite_As_Binary_Comparison& operator && ( RhsT const& );
+    template<typename RhsT> STATIC_ASSERT_Expression_Too_Complex_Please_Rewrite_As_Binary_Comparison& operator || ( RhsT const& );
+
+private:
+    template<Internal::Operator Op, typename RhsT>
+    ResultBuilder& captureExpression( RhsT const& rhs ) {
+        return m_rb
+            .setResultType( Internal::compare<Op>( m_lhs, rhs ) )
+            .setLhs( Catch::toString( m_lhs ) )
+            .setRhs( Catch::toString( rhs ) )
+            .setOp( Internal::OperatorTraits<Op>::getName() );
+    }
+
+private:
+    ResultBuilder& m_rb;
+    T m_lhs;
+};
+
+} // end namespace Catch
+
+
+namespace Catch {
+
+    template<typename T>
+    inline ExpressionLhs<T const&> ResultBuilder::operator <= ( T const& operand ) {
+        return ExpressionLhs<T const&>( *this, operand );
+    }
+
+    inline ExpressionLhs<bool> ResultBuilder::operator <= ( bool value ) {
+        return ExpressionLhs<bool>( *this, value );
+    }
+
+} // namespace Catch
+
+// #included from: catch_message.h
+#define TWOBLUECUBES_CATCH_MESSAGE_H_INCLUDED
+
+#include <string>
+
+namespace Catch {
+
+    struct MessageInfo {
+        MessageInfo(    std::string const& _macroName,
+                        SourceLineInfo const& _lineInfo,
+                        ResultWas::OfType _type );
+
+        std::string macroName;
+        SourceLineInfo lineInfo;
+        ResultWas::OfType type;
+        std::string message;
+        unsigned int sequence;
+
+        bool operator == ( MessageInfo const& other ) const {
+            return sequence == other.sequence;
+        }
+        bool operator < ( MessageInfo const& other ) const {
+            return sequence < other.sequence;
+        }
+    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 T>
+        MessageBuilder& operator << ( T const& value ) {
+            m_stream << value;
+            return *this;
+        }
+
+        MessageInfo m_info;
+        std::ostringstream m_stream;
+    };
+
+    class ScopedMessage {
+    public:
+        ScopedMessage( MessageBuilder const& builder );
+        ScopedMessage( ScopedMessage const& other );
+        ~ScopedMessage();
+
+        MessageInfo m_info;
+    };
+
+} // end namespace Catch
+
+// #included from: catch_interfaces_capture.h
+#define TWOBLUECUBES_CATCH_INTERFACES_CAPTURE_H_INCLUDED
+
+#include <string>
+
+namespace Catch {
+
+    class TestCase;
+    class AssertionResult;
+    struct AssertionInfo;
+    struct SectionInfo;
+    struct MessageInfo;
+    class ScopedMessageBuilder;
+    struct Counts;
+
+    struct IResultCapture {
+
+        virtual ~IResultCapture();
+
+        virtual void assertionEnded( AssertionResult const& result ) = 0;
+        virtual bool sectionStarted(    SectionInfo const& sectionInfo,
+                                        Counts& assertions ) = 0;
+        virtual void sectionEnded( SectionInfo const& name, Counts const& assertions, double _durationInSeconds ) = 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 void handleFatalErrorCondition( std::string const& message ) = 0;
+    };
+
+    IResultCapture& getResultCapture();
+}
+
+// #included from: catch_debugger.h
+#define TWOBLUECUBES_CATCH_DEBUGGER_H_INCLUDED
+
+// #included from: catch_platform.h
+#define TWOBLUECUBES_CATCH_PLATFORM_H_INCLUDED
+
+#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(WIN32) || defined(__WIN32__) || defined(_WIN32) || defined(_MSC_VER)
+#define CATCH_PLATFORM_WINDOWS
+#endif
+
+#include <string>
+
+namespace Catch{
+
+    bool isDebuggerActive();
+    void writeToDebugConsole( std::string const& text );
+}
+
+#ifdef CATCH_PLATFORM_MAC
+
+    // The following code snippet based on:
+    // http://cocoawithlove.com/2008/03/break-into-debugger.html
+    #ifdef DEBUG
+        #if defined(__ppc64__) || defined(__ppc__)
+            #define CATCH_BREAK_INTO_DEBUGGER() \
+                if( Catch::isDebuggerActive() ) { \
+                    __asm__("li r0, 20\nsc\nnop\nli r0, 37\nli r4, 2\nsc\nnop\n" \
+                    : : : "memory","r0","r3","r4" ); \
+                }
+        #else
+            #define CATCH_BREAK_INTO_DEBUGGER() if( Catch::isDebuggerActive() ) {__asm__("int $3\n" : : );}
+        #endif
+    #endif
+
+#elif defined(_MSC_VER)
+    #define CATCH_BREAK_INTO_DEBUGGER() if( Catch::isDebuggerActive() ) { __debugbreak(); }
+#elif defined(__MINGW32__)
+    extern "C" __declspec(dllimport) void __stdcall DebugBreak();
+    #define CATCH_BREAK_INTO_DEBUGGER() if( Catch::isDebuggerActive() ) { DebugBreak(); }
+#endif
+
+#ifndef CATCH_BREAK_INTO_DEBUGGER
+#define CATCH_BREAK_INTO_DEBUGGER() Catch::alwaysTrue();
+#endif
+
+// #included from: catch_interfaces_runner.h
+#define TWOBLUECUBES_CATCH_INTERFACES_RUNNER_H_INCLUDED
+
+namespace Catch {
+    class TestCase;
+
+    struct IRunner {
+        virtual ~IRunner();
+        virtual bool aborting() const = 0;
+    };
+}
+
+///////////////////////////////////////////////////////////////////////////////
+// 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();
+
+///////////////////////////////////////////////////////////////////////////////
+#define INTERNAL_CATCH_TEST( expr, resultDisposition, macroName ) \
+    do { \
+        Catch::ResultBuilder __catchResult( macroName, CATCH_INTERNAL_LINEINFO, #expr, resultDisposition ); \
+        try { \
+            ( __catchResult <= expr ).endExpression(); \
+        } \
+        catch( ... ) { \
+            __catchResult.useActiveException( Catch::ResultDisposition::Normal ); \
+        } \
+        INTERNAL_CATCH_REACT( __catchResult ) \
+    } while( Catch::isTrue( false && (expr) ) ) // expr here is never evaluated at runtime but it forces the compiler to give it a look
+
+///////////////////////////////////////////////////////////////////////////////
+#define INTERNAL_CATCH_IF( expr, resultDisposition, macroName ) \
+    INTERNAL_CATCH_TEST( expr, resultDisposition, macroName ); \
+    if( Catch::getResultCapture().getLastResult()->succeeded() )
+
+///////////////////////////////////////////////////////////////////////////////
+#define INTERNAL_CATCH_ELSE( expr, resultDisposition, macroName ) \
+    INTERNAL_CATCH_TEST( expr, resultDisposition, macroName ); \
+    if( !Catch::getResultCapture().getLastResult()->succeeded() )
+
+///////////////////////////////////////////////////////////////////////////////
+#define INTERNAL_CATCH_NO_THROW( expr, resultDisposition, macroName ) \
+    do { \
+        Catch::ResultBuilder __catchResult( macroName, CATCH_INTERNAL_LINEINFO, #expr, resultDisposition ); \
+        try { \
+            expr; \
+            __catchResult.captureResult( Catch::ResultWas::Ok ); \
+        } \
+        catch( ... ) { \
+            __catchResult.useActiveException( resultDisposition ); \
+        } \
+        INTERNAL_CATCH_REACT( __catchResult ) \
+    } while( Catch::alwaysFalse() )
+
+///////////////////////////////////////////////////////////////////////////////
+#define INTERNAL_CATCH_THROWS( expr, resultDisposition, macroName ) \
+    do { \
+        Catch::ResultBuilder __catchResult( macroName, CATCH_INTERNAL_LINEINFO, #expr, resultDisposition ); \
+        if( __catchResult.allowThrows() ) \
+            try { \
+                expr; \
+                __catchResult.captureResult( Catch::ResultWas::DidntThrowException ); \
+            } \
+            catch( ... ) { \
+                __catchResult.captureResult( Catch::ResultWas::Ok ); \
+            } \
+        else \
+            __catchResult.captureResult( Catch::ResultWas::Ok ); \
+        INTERNAL_CATCH_REACT( __catchResult ) \
+    } while( Catch::alwaysFalse() )
+
+///////////////////////////////////////////////////////////////////////////////
+#define INTERNAL_CATCH_THROWS_AS( expr, exceptionType, resultDisposition, macroName ) \
+    do { \
+        Catch::ResultBuilder __catchResult( macroName, CATCH_INTERNAL_LINEINFO, #expr, resultDisposition ); \
+        if( __catchResult.allowThrows() ) \
+            try { \
+                expr; \
+                __catchResult.captureResult( Catch::ResultWas::DidntThrowException ); \
+            } \
+            catch( exceptionType ) { \
+                __catchResult.captureResult( Catch::ResultWas::Ok ); \
+            } \
+            catch( ... ) { \
+                __catchResult.useActiveException( resultDisposition ); \
+            } \
+        else \
+            __catchResult.captureResult( Catch::ResultWas::Ok ); \
+        INTERNAL_CATCH_REACT( __catchResult ) \
+    } while( Catch::alwaysFalse() )
+
+///////////////////////////////////////////////////////////////////////////////
+#ifdef CATCH_CONFIG_VARIADIC_MACROS
+    #define INTERNAL_CATCH_MSG( messageType, resultDisposition, macroName, ... ) \
+        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( messageType, resultDisposition, macroName, 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_INFO( log, macroName ) \
+    Catch::ScopedMessage INTERNAL_CATCH_UNIQUE_NAME( scopedMessage ) = Catch::MessageBuilder( macroName, CATCH_INTERNAL_LINEINFO, Catch::ResultWas::Info ) << log;
+
+///////////////////////////////////////////////////////////////////////////////
+#define INTERNAL_CHECK_THAT( arg, matcher, resultDisposition, macroName ) \
+    do { \
+        Catch::ResultBuilder __catchResult( macroName, CATCH_INTERNAL_LINEINFO, #arg " " #matcher, resultDisposition ); \
+        try { \
+            std::string matcherAsString = ::Catch::Matchers::matcher.toString(); \
+            __catchResult \
+                .setLhs( Catch::toString( arg ) ) \
+                .setRhs( matcherAsString == Catch::Detail::unprintableString ? #matcher : matcherAsString ) \
+                .setOp( "matches" ) \
+                .setResultType( ::Catch::Matchers::matcher.match( arg ) ); \
+            __catchResult.captureExpression(); \
+        } catch( ... ) { \
+            __catchResult.useActiveException( resultDisposition | Catch::ResultDisposition::ContinueOnFailure ); \
+        } \
+        INTERNAL_CATCH_REACT( __catchResult ) \
+    } while( Catch::alwaysFalse() )
+
+// #included from: internal/catch_section.h
+#define TWOBLUECUBES_CATCH_SECTION_H_INCLUDED
+
+// #included from: catch_section_info.h
+#define TWOBLUECUBES_CATCH_SECTION_INFO_H_INCLUDED
+
+namespace Catch {
+
+    struct SectionInfo {
+        SectionInfo
+            (   SourceLineInfo const& _lineInfo,
+                std::string const& _name,
+                std::string const& _description = std::string() );
+
+        std::string name;
+        std::string description;
+        SourceLineInfo lineInfo;
+    };
+
+} // end namespace Catch
+
+// #included from: catch_totals.hpp
+#define TWOBLUECUBES_CATCH_TOTALS_HPP_INCLUDED
+
+#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;
+        }
+
+        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 passed;
+        std::size_t failed;
+        std::size_t failedButOk;
+    };
+
+    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 ) {
+            assertions += other.assertions;
+            testCases += other.testCases;
+            return *this;
+        }
+
+        Counts assertions;
+        Counts testCases;
+    };
+}
+
+// #included from: catch_timer.h
+#define TWOBLUECUBES_CATCH_TIMER_H_INCLUDED
+
+#ifdef CATCH_PLATFORM_WINDOWS
+typedef unsigned long long uint64_t;
+#else
+#include <stdint.h>
+#endif
+
+namespace Catch {
+
+    class Timer {
+    public:
+        Timer() : m_ticks( 0 ) {}
+        void start();
+        unsigned int getElapsedMicroseconds() const;
+        unsigned int getElapsedMilliseconds() const;
+        double getElapsedSeconds() const;
+
+    private:
+        uint64_t m_ticks;
+    };
+
+} // namespace Catch
+
+#include <string>
+
+namespace Catch {
+
+    class Section : NonCopyable {
+    public:
+        Section( SectionInfo const& info );
+        ~Section();
+
+        // This indicates whether the section should be executed or not
+        operator bool() const;
+
+    private:
+        SectionInfo m_info;
+
+        std::string m_name;
+        Counts m_assertions;
+        bool m_sectionIncluded;
+        Timer m_timer;
+    };
+
+} // 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
+
+// #included from: internal/catch_generators.hpp
+#define TWOBLUECUBES_CATCH_GENERATORS_HPP_INCLUDED
+
+#include <iterator>
+#include <vector>
+#include <string>
+#include <stdlib.h>
+
+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 ){}
+
+    virtual T getValue( std::size_t index ) const {
+        return m_from+static_cast<int>( index );
+    }
+
+    virtual std::size_t size() const {
+        return static_cast<std::size_t>( 1+m_to-m_from );
+    }
+
+private:
+
+    T m_from;
+    T m_to;
+};
+
+template<typename T>
+class ValuesGenerator : public IGenerator<T> {
+public:
+    ValuesGenerator(){}
+
+    void add( T value ) {
+        m_values.push_back( value );
+    }
+
+    virtual T getValue( std::size_t index ) const {
+        return m_values[index];
+    }
+
+    virtual std::size_t size() const {
+        return m_values.size();
+    }
+
+private:
+    std::vector<T> m_values;
+};
+
+template<typename T>
+class CompositeGenerator {
+public:
+    CompositeGenerator() : m_totalSize( 0 ) {}
+
+    // *** Move semantics, similar to auto_ptr ***
+    CompositeGenerator( CompositeGenerator& other )
+    :   m_fileInfo( other.m_fileInfo ),
+        m_totalSize( 0 )
+    {
+        move( other );
+    }
+
+    CompositeGenerator& setFileInfo( const char* fileInfo ) {
+        m_fileInfo = fileInfo;
+        return *this;
+    }
+
+    ~CompositeGenerator() {
+        deleteAll( m_composed );
+    }
+
+    operator T () const {
+        size_t overallIndex = getCurrentContext().getGeneratorIndex( m_fileInfo, m_totalSize );
+
+        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
+    }
+
+    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 ) {
+        std::copy( other.m_composed.begin(), other.m_composed.end(), std::back_inserter( m_composed ) );
+        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>
+// #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 IRegistryHub {
+        virtual ~IRegistryHub();
+
+        virtual IReporterRegistry const& getReporterRegistry() const = 0;
+        virtual ITestCaseRegistry const& getTestCaseRegistry() const = 0;
+        virtual IExceptionTranslatorRegistry& getExceptionTranslatorRegistry() = 0;
+    };
+
+    struct IMutableRegistryHub {
+        virtual ~IMutableRegistryHub();
+        virtual void registerReporter( std::string const& name, IReporterFactory* factory ) = 0;
+        virtual void registerTest( TestCase const& testInfo ) = 0;
+        virtual void registerTranslator( const IExceptionTranslator* translator ) = 0;
+    };
+
+    IRegistryHub& getRegistryHub();
+    IMutableRegistryHub& getMutableRegistryHub();
+    void cleanUp();
+    std::string translateActiveException();
+
+}
+
+
+namespace Catch {
+
+    typedef std::string(*exceptionTranslateFunction)();
+
+    struct IExceptionTranslator {
+        virtual ~IExceptionTranslator();
+        virtual std::string translate() const = 0;
+    };
+
+    struct IExceptionTranslatorRegistry {
+        virtual ~IExceptionTranslatorRegistry();
+
+        virtual std::string translateActiveException() const = 0;
+    };
+
+    class ExceptionTranslatorRegistrar {
+        template<typename T>
+        class ExceptionTranslator : public IExceptionTranslator {
+        public:
+
+            ExceptionTranslator( std::string(*translateFunction)( T& ) )
+            : m_translateFunction( translateFunction )
+            {}
+
+            virtual std::string translate() const {
+                try {
+                    throw;
+                }
+                catch( T& ex ) {
+                    return m_translateFunction( ex );
+                }
+            }
+
+        protected:
+            std::string(*m_translateFunction)( T& );
+        };
+
+    public:
+        template<typename T>
+        ExceptionTranslatorRegistrar( std::string(*translateFunction)( T& ) ) {
+            getMutableRegistryHub().registerTranslator
+                ( new ExceptionTranslator<T>( translateFunction ) );
+        }
+    };
+}
+
+///////////////////////////////////////////////////////////////////////////////
+#define INTERNAL_CATCH_TRANSLATE_EXCEPTION( signature ) \
+    static std::string INTERNAL_CATCH_UNIQUE_NAME( catch_internal_ExceptionTranslator )( signature ); \
+    namespace{ Catch::ExceptionTranslatorRegistrar INTERNAL_CATCH_UNIQUE_NAME( catch_internal_ExceptionRegistrar )( &INTERNAL_CATCH_UNIQUE_NAME( catch_internal_ExceptionTranslator ) ); }\
+    static std::string INTERNAL_CATCH_UNIQUE_NAME(  catch_internal_ExceptionTranslator )( signature )
+
+// #included from: internal/catch_approx.hpp
+#define TWOBLUECUBES_CATCH_APPROX_HPP_INCLUDED
+
+#include <cmath>
+#include <limits>
+
+namespace Catch {
+namespace Detail {
+
+    class Approx {
+    public:
+        explicit Approx ( double value )
+        :   m_epsilon( std::numeric_limits<float>::epsilon()*100 ),
+            m_scale( 1.0 ),
+            m_value( value )
+        {}
+
+        Approx( Approx const& other )
+        :   m_epsilon( other.m_epsilon ),
+            m_scale( other.m_scale ),
+            m_value( other.m_value )
+        {}
+
+        static Approx custom() {
+            return Approx( 0 );
+        }
+
+        Approx operator()( double value ) {
+            Approx approx( value );
+            approx.epsilon( m_epsilon );
+            approx.scale( m_scale );
+            return approx;
+        }
+
+        friend bool operator == ( double lhs, Approx const& rhs ) {
+            // Thanks to Richard Harris for his help refining this formula
+            return fabs( lhs - rhs.m_value ) < rhs.m_epsilon * (rhs.m_scale + (std::max)( fabs(lhs), fabs(rhs.m_value) ) );
+        }
+
+        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 );
+        }
+
+        Approx& epsilon( double newEpsilon ) {
+            m_epsilon = newEpsilon;
+            return *this;
+        }
+
+        Approx& scale( double newScale ) {
+            m_scale = newScale;
+            return *this;
+        }
+
+        std::string toString() const {
+            std::ostringstream oss;
+            oss << "Approx( " << Catch::toString( m_value ) << " )";
+            return oss.str();
+        }
+
+    private:
+        double m_epsilon;
+        double m_scale;
+        double m_value;
+    };
+}
+
+template<>
+inline std::string toString<Detail::Approx>( Detail::Approx const& value ) {
+    return value.toString();
+}
+
+} // end namespace Catch
+
+// #included from: internal/catch_matchers.hpp
+#define TWOBLUECUBES_CATCH_MATCHERS_HPP_INCLUDED
+
+namespace Catch {
+namespace Matchers {
+    namespace Impl {
+
+    template<typename ExpressionT>
+    struct Matcher : SharedImpl<IShared>
+    {
+        typedef ExpressionT ExpressionType;
+
+        virtual ~Matcher() {}
+        virtual Ptr<Matcher> clone() const = 0;
+        virtual bool match( ExpressionT const& expr ) const = 0;
+        virtual std::string toString() const = 0;
+    };
+
+    template<typename DerivedT, typename ExpressionT>
+    struct MatcherImpl : Matcher<ExpressionT> {
+
+        virtual Ptr<Matcher<ExpressionT> > clone() const {
+            return Ptr<Matcher<ExpressionT> >( new DerivedT( static_cast<DerivedT const&>( *this ) ) );
+        }
+    };
+
+    namespace Generic {
+
+        template<typename ExpressionT>
+        class AllOf : public MatcherImpl<AllOf<ExpressionT>, ExpressionT> {
+        public:
+
+            AllOf() {}
+            AllOf( AllOf const& other ) : m_matchers( other.m_matchers ) {}
+
+            AllOf& add( Matcher<ExpressionT> const& matcher ) {
+                m_matchers.push_back( matcher.clone() );
+                return *this;
+            }
+            virtual bool match( ExpressionT const& expr ) const
+            {
+                for( std::size_t i = 0; i < m_matchers.size(); ++i )
+                    if( !m_matchers[i]->match( expr ) )
+                        return false;
+                return true;
+            }
+            virtual std::string toString() const {
+                std::ostringstream oss;
+                oss << "( ";
+                for( std::size_t i = 0; i < m_matchers.size(); ++i ) {
+                    if( i != 0 )
+                        oss << " and ";
+                    oss << m_matchers[i]->toString();
+                }
+                oss << " )";
+                return oss.str();
+            }
+
+        private:
+            std::vector<Ptr<Matcher<ExpressionT> > > m_matchers;
+        };
+
+        template<typename ExpressionT>
+        class AnyOf : public MatcherImpl<AnyOf<ExpressionT>, ExpressionT> {
+        public:
+
+            AnyOf() {}
+            AnyOf( AnyOf const& other ) : m_matchers( other.m_matchers ) {}
+
+            AnyOf& add( Matcher<ExpressionT> const& matcher ) {
+                m_matchers.push_back( matcher.clone() );
+                return *this;
+            }
+            virtual bool match( ExpressionT const& expr ) const
+            {
+                for( std::size_t i = 0; i < m_matchers.size(); ++i )
+                    if( m_matchers[i]->match( expr ) )
+                        return true;
+                return false;
+            }
+            virtual std::string toString() const {
+                std::ostringstream oss;
+                oss << "( ";
+                for( std::size_t i = 0; i < m_matchers.size(); ++i ) {
+                    if( i != 0 )
+                        oss << " or ";
+                    oss << m_matchers[i]->toString();
+                }
+                oss << " )";
+                return oss.str();
+            }
+
+        private:
+            std::vector<Ptr<Matcher<ExpressionT> > > m_matchers;
+        };
+
+    }
+
+    namespace StdString {
+
+        inline std::string makeString( std::string const& str ) { return str; }
+        inline std::string makeString( const char* str ) { return str ? std::string( str ) : std::string(); }
+
+        struct Equals : MatcherImpl<Equals, std::string> {
+            Equals( std::string const& str ) : m_str( str ){}
+            Equals( Equals const& other ) : m_str( other.m_str ){}
+
+            virtual ~Equals();
+
+            virtual bool match( std::string const& expr ) const {
+                return m_str == expr;
+            }
+            virtual std::string toString() const {
+                return "equals: \"" + m_str + "\"";
+            }
+
+            std::string m_str;
+        };
+
+        struct Contains : MatcherImpl<Contains, std::string> {
+            Contains( std::string const& substr ) : m_substr( substr ){}
+            Contains( Contains const& other ) : m_substr( other.m_substr ){}
+
+            virtual ~Contains();
+
+            virtual bool match( std::string const& expr ) const {
+                return expr.find( m_substr ) != std::string::npos;
+            }
+            virtual std::string toString() const {
+                return "contains: \"" + m_substr + "\"";
+            }
+
+            std::string m_substr;
+        };
+
+        struct StartsWith : MatcherImpl<StartsWith, std::string> {
+            StartsWith( std::string const& substr ) : m_substr( substr ){}
+            StartsWith( StartsWith const& other ) : m_substr( other.m_substr ){}
+
+            virtual ~StartsWith();
+
+            virtual bool match( std::string const& expr ) const {
+                return expr.find( m_substr ) == 0;
+            }
+            virtual std::string toString() const {
+                return "starts with: \"" + m_substr + "\"";
+            }
+
+            std::string m_substr;
+        };
+
+        struct EndsWith : MatcherImpl<EndsWith, std::string> {
+            EndsWith( std::string const& substr ) : m_substr( substr ){}
+            EndsWith( EndsWith const& other ) : m_substr( other.m_substr ){}
+
+            virtual ~EndsWith();
+
+            virtual bool match( std::string const& expr ) const {
+                return expr.find( m_substr ) == expr.size() - m_substr.size();
+            }
+            virtual std::string toString() const {
+                return "ends with: \"" + m_substr + "\"";
+            }
+
+            std::string m_substr;
+        };
+    } // namespace StdString
+    } // namespace Impl
+
+    // The following functions create the actual matcher objects.
+    // This allows the types to be inferred
+    template<typename ExpressionT>
+    inline Impl::Generic::AllOf<ExpressionT> AllOf( Impl::Matcher<ExpressionT> const& m1,
+                                                    Impl::Matcher<ExpressionT> const& m2 ) {
+        return Impl::Generic::AllOf<ExpressionT>().add( m1 ).add( m2 );
+    }
+    template<typename ExpressionT>
+    inline Impl::Generic::AllOf<ExpressionT> AllOf( Impl::Matcher<ExpressionT> const& m1,
+                                                    Impl::Matcher<ExpressionT> const& m2,
+                                                    Impl::Matcher<ExpressionT> const& m3 ) {
+        return Impl::Generic::AllOf<ExpressionT>().add( m1 ).add( m2 ).add( m3 );
+    }
+    template<typename ExpressionT>
+    inline Impl::Generic::AnyOf<ExpressionT> AnyOf( Impl::Matcher<ExpressionT> const& m1,
+                                                    Impl::Matcher<ExpressionT> const& m2 ) {
+        return Impl::Generic::AnyOf<ExpressionT>().add( m1 ).add( m2 );
+    }
+    template<typename ExpressionT>
+    inline Impl::Generic::AnyOf<ExpressionT> AnyOf( Impl::Matcher<ExpressionT> const& m1,
+                                                    Impl::Matcher<ExpressionT> const& m2,
+                                                    Impl::Matcher<ExpressionT> const& m3 ) {
+        return Impl::Generic::AnyOf<ExpressionT>().add( m1 ).add( m2 ).add( m3 );
+    }
+
+    inline Impl::StdString::Equals      Equals( std::string const& str ) {
+        return Impl::StdString::Equals( str );
+    }
+    inline Impl::StdString::Equals      Equals( const char* str ) {
+        return Impl::StdString::Equals( Impl::StdString::makeString( str ) );
+    }
+    inline Impl::StdString::Contains    Contains( std::string const& substr ) {
+        return Impl::StdString::Contains( substr );
+    }
+    inline Impl::StdString::Contains    Contains( const char* substr ) {
+        return Impl::StdString::Contains( Impl::StdString::makeString( substr ) );
+    }
+    inline Impl::StdString::StartsWith  StartsWith( std::string const& substr ) {
+        return Impl::StdString::StartsWith( substr );
+    }
+    inline Impl::StdString::StartsWith  StartsWith( const char* substr ) {
+        return Impl::StdString::StartsWith( Impl::StdString::makeString( substr ) );
+    }
+    inline Impl::StdString::EndsWith    EndsWith( std::string const& substr ) {
+        return Impl::StdString::EndsWith( substr );
+    }
+    inline Impl::StdString::EndsWith    EndsWith( const char* substr ) {
+        return Impl::StdString::EndsWith( Impl::StdString::makeString( substr ) );
+    }
+
+} // namespace Matchers
+
+using namespace Matchers;
+
+} // namespace Catch
+
+// #included from: internal/catch_interfaces_tag_alias_registry.h
+#define TWOBLUECUBES_CATCH_INTERFACES_TAG_ALIAS_REGISTRY_H_INCLUDED
+
+// #included from: catch_tag_alias.h
+#define TWOBLUECUBES_CATCH_TAG_ALIAS_H_INCLUDED
+
+#include <string>
+
+namespace Catch {
+
+    struct TagAlias {
+        TagAlias( std::string _tag, SourceLineInfo _lineInfo ) : tag( _tag ), lineInfo( _lineInfo ) {}
+
+        std::string tag;
+        SourceLineInfo lineInfo;
+    };
+
+    struct RegistrarForTagAliases {
+        RegistrarForTagAliases( char const* alias, char const* tag, SourceLineInfo const& lineInfo );
+    };
+
+} // end namespace Catch
+
+#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
+
+namespace Catch {
+
+    // An optional type
+    template<typename T>
+    class Option {
+    public:
+        Option() : nullableValue( NULL ) {}
+        Option( T const& _value )
+        : nullableValue( new( storage ) T( _value ) )
+        {}
+        Option( Option const& _other )
+        : nullableValue( _other ? new( storage ) T( *_other ) : NULL )
+        {}
+
+        ~Option() {
+            reset();
+        }
+
+        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 reset() {
+            if( nullableValue )
+                nullableValue->~T();
+            nullableValue = NULL;
+        }
+
+        T& operator*() { return *nullableValue; }
+        T const& operator*() const { return *nullableValue; }
+        T* operator->() { return nullableValue; }
+        const T* operator->() const { return nullableValue; }
+
+        T valueOr( T const& defaultValue ) const {
+            return nullableValue ? *nullableValue : defaultValue;
+        }
+
+        bool some() const { return nullableValue != NULL; }
+        bool none() const { return nullableValue == NULL; }
+
+        bool operator !() const { return nullableValue == NULL; }
+        operator SafeBool::type() const {
+            return SafeBool::makeSafe( some() );
+        }
+
+    private:
+        T* nullableValue;
+        char storage[sizeof(T)];
+    };
+
+} // end namespace Catch
+
+namespace Catch {
+
+    struct ITagAliasRegistry {
+        virtual ~ITagAliasRegistry();
+        virtual Option<TagAlias> find( std::string const& alias ) const = 0;
+        virtual std::string expandAliases( std::string const& unexpandedTestSpec ) const = 0;
+
+        static ITagAliasRegistry const& get();
+    };
+
+} // end 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
+
+#include <string>
+#include <set>
+
+#ifdef __clang__
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wpadded"
+#endif
+
+namespace Catch {
+
+    struct ITestCase;
+
+    struct TestCaseInfo {
+        enum SpecialProperties{
+            None = 0,
+            IsHidden = 1 << 1,
+            ShouldFail = 1 << 2,
+            MayFail = 1 << 3,
+            Throws = 1 << 4
+        };
+
+        TestCaseInfo(   std::string const& _name,
+                        std::string const& _className,
+                        std::string const& _description,
+                        std::set<std::string> const& _tags,
+                        SourceLineInfo const& _lineInfo );
+
+        TestCaseInfo( TestCaseInfo const& other );
+
+        bool isHidden() const;
+        bool throws() const;
+        bool okToFail() const;
+        bool expectedToFail() const;
+
+        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;
+    };
+
+    class TestCase : public TestCaseInfo {
+    public:
+
+        TestCase( ITestCase* testCase, TestCaseInfo const& info );
+        TestCase( TestCase const& other );
+
+        TestCase withName( std::string const& _newName ) const;
+
+        void invoke() const;
+
+        TestCaseInfo const& getTestCaseInfo() const;
+
+        void swap( TestCase& other );
+        bool operator == ( TestCase const& other ) const;
+        bool operator < ( TestCase const& other ) const;
+        TestCase& operator = ( TestCase const& other );
+
+    private:
+        Ptr<ITestCase> test;
+    };
+
+    TestCase makeTestCase(  ITestCase* testCase,
+                            std::string const& className,
+                            std::string const& name,
+                            std::string const& description,
+                            SourceLineInfo const& lineInfo );
+}
+
+#ifdef __clang__
+#pragma clang diagnostic pop
+#endif
+
+
+#ifdef __OBJC__
+// #included from: internal/catch_objc.hpp
+#define TWOBLUECUBES_CATCH_OBJC_HPP_INCLUDED
+
+#import <objc/runtime.h>
+
+#include <string>
+
+// 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
+
+///////////////////////////////////////////////////////////////////////////////
+// This protocol is really only here for (self) documenting purposes, since
+// all its methods are optional.
+ at protocol OcFixture
+
+ at optional
+
+-(void) setUp;
+-(void) tearDown;
+
+ at end
+
+namespace Catch {
+
+    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() {}
+
+        Class m_cls;
+        SEL m_sel;
+    };
+
+    namespace Detail{
+
+        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 "";
+        }
+    }
+
+    inline size_t registerTestMethods() {
+        size_t noTestMethods = 0;
+        int noClasses = objc_getClassList( NULL, 0 );
+
+        Class* classes = (CATCH_UNSAFE_UNRETAINED Class *)malloc( sizeof(Class) * noClasses);
+        objc_getClassList( classes, noClasses );
+
+        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 );
+
+                        getMutableRegistryHub().registerTest( makeTestCase( new OcMethod( cls, selector ), className, name.c_str(), desc.c_str(), SourceLineInfo() ) );
+                        noTestMethods++;
+                    }
+                }
+                free(methods);
+            }
+        }
+        return noTestMethods;
+    }
+
+    namespace Matchers {
+        namespace Impl {
+        namespace NSStringMatchers {
+
+            template<typename MatcherT>
+            struct StringHolder : MatcherImpl<MatcherT, NSString*>{
+                StringHolder( NSString* substr ) : m_substr( [substr copy] ){}
+                StringHolder( StringHolder const& other ) : m_substr( [other.m_substr copy] ){}
+                StringHolder() {
+                    arcSafeRelease( m_substr );
+                }
+
+                NSString* m_substr;
+            };
+
+            struct Equals : StringHolder<Equals> {
+                Equals( NSString* substr ) : StringHolder( substr ){}
+
+                virtual bool match( ExpressionType const& str ) const {
+                    return  (str != nil || m_substr == nil ) &&
+                            [str isEqualToString:m_substr];
+                }
+
+                virtual std::string toString() const {
+                    return "equals string: " + Catch::toString( m_substr );
+                }
+            };
+
+            struct Contains : StringHolder<Contains> {
+                Contains( NSString* substr ) : StringHolder( substr ){}
+
+                virtual bool match( ExpressionType const& str ) const {
+                    return  (str != nil || m_substr == nil ) &&
+                            [str rangeOfString:m_substr].location != NSNotFound;
+                }
+
+                virtual std::string toString() const {
+                    return "contains string: " + Catch::toString( m_substr );
+                }
+            };
+
+            struct StartsWith : StringHolder<StartsWith> {
+                StartsWith( NSString* substr ) : StringHolder( substr ){}
+
+                virtual bool match( ExpressionType const& str ) const {
+                    return  (str != nil || m_substr == nil ) &&
+                            [str rangeOfString:m_substr].location == 0;
+                }
+
+                virtual std::string toString() const {
+                    return "starts with: " + Catch::toString( m_substr );
+                }
+            };
+            struct EndsWith : StringHolder<EndsWith> {
+                EndsWith( NSString* substr ) : StringHolder( substr ){}
+
+                virtual bool match( ExpressionType const& str ) const {
+                    return  (str != nil || m_substr == nil ) &&
+                            [str rangeOfString:m_substr].location == [str length] - [m_substr length];
+                }
+
+                virtual std::string toString() const {
+                    return "ends with: " + Catch::toString( m_substr );
+                }
+            };
+
+        } // namespace NSStringMatchers
+        } // namespace Impl
+
+        inline Impl::NSStringMatchers::Equals
+            Equals( NSString* substr ){ return Impl::NSStringMatchers::Equals( substr ); }
+
+        inline Impl::NSStringMatchers::Contains
+            Contains( NSString* substr ){ return Impl::NSStringMatchers::Contains( substr ); }
+
+        inline Impl::NSStringMatchers::StartsWith
+            StartsWith( NSString* substr ){ return Impl::NSStringMatchers::StartsWith( substr ); }
+
+        inline Impl::NSStringMatchers::EndsWith
+            EndsWith( NSString* substr ){ return Impl::NSStringMatchers::EndsWith( substr ); }
+
+    } // namespace Matchers
+
+    using namespace Matchers;
+
+} // 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
+
+#ifdef CATCH_IMPL
+// #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
+
+#ifdef __clang__
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wweak-vtables"
+#endif
+
+// #included from: ../catch_runner.hpp
+#define TWOBLUECUBES_CATCH_RUNNER_HPP_INCLUDED
+
+// #included from: internal/catch_commandline.hpp
+#define TWOBLUECUBES_CATCH_COMMANDLINE_HPP_INCLUDED
+
+// #included from: catch_config.hpp
+#define TWOBLUECUBES_CATCH_CONFIG_HPP_INCLUDED
+
+// #included from: catch_test_spec_parser.hpp
+#define TWOBLUECUBES_CATCH_TEST_SPEC_PARSER_HPP_INCLUDED
+
+#ifdef __clang__
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wpadded"
+#endif
+
+// #included from: catch_test_spec.hpp
+#define TWOBLUECUBES_CATCH_TEST_SPEC_HPP_INCLUDED
+
+#ifdef __clang__
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wpadded"
+#endif
+
+#include <string>
+#include <vector>
+
+namespace Catch {
+
+    class TestSpec {
+        struct Pattern : SharedImpl<> {
+            virtual ~Pattern();
+            virtual bool matches( TestCaseInfo const& testCase ) const = 0;
+        };
+        class NamePattern : public Pattern {
+            enum WildcardPosition {
+                NoWildcard = 0,
+                WildcardAtStart = 1,
+                WildcardAtEnd = 2,
+                WildcardAtBothEnds = WildcardAtStart | WildcardAtEnd
+            };
+
+        public:
+            NamePattern( std::string const& name ) : m_name( toLower( name ) ), m_wildcard( NoWildcard ) {
+                if( startsWith( m_name, "*" ) ) {
+                    m_name = m_name.substr( 1 );
+                    m_wildcard = WildcardAtStart;
+                }
+                if( endsWith( m_name, "*" ) ) {
+                    m_name = m_name.substr( 0, m_name.size()-1 );
+                    m_wildcard = static_cast<WildcardPosition>( m_wildcard | WildcardAtEnd );
+                }
+            }
+            virtual ~NamePattern();
+            virtual bool matches( TestCaseInfo const& testCase ) const {
+                switch( m_wildcard ) {
+                    case NoWildcard:
+                        return m_name == toLower( testCase.name );
+                    case WildcardAtStart:
+                        return endsWith( toLower( testCase.name ), m_name );
+                    case WildcardAtEnd:
+                        return startsWith( toLower( testCase.name ), m_name );
+                    case WildcardAtBothEnds:
+                        return contains( toLower( testCase.name ), m_name );
+                }
+
+#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 m_name;
+            WildcardPosition m_wildcard;
+        };
+        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;
+        };
+        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;
+        };
+
+        struct Filter {
+            std::vector<Ptr<Pattern> > m_patterns;
+
+            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;
+            }
+        };
+
+    public:
+        bool hasFilters() const {
+            return !m_filters.empty();
+        }
+        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;
+            return false;
+        }
+
+    private:
+        std::vector<Filter> m_filters;
+
+        friend class TestSpecParser;
+    };
+}
+
+#ifdef __clang__
+#pragma clang diagnostic pop
+#endif
+
+namespace Catch {
+
+    class TestSpecParser {
+        enum Mode{ None, Name, QuotedName, Tag };
+        Mode m_mode;
+        bool m_exclusion;
+        std::size_t m_start, m_pos;
+        std::string m_arg;
+        TestSpec::Filter m_currentFilter;
+        TestSpec m_testSpec;
+        ITagAliasRegistry const* m_tagAliases;
+
+    public:
+        TestSpecParser( ITagAliasRegistry const& tagAliases ) : 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 );
+            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;
+        }
+        TestSpec testSpec() {
+            addFilter();
+            return m_testSpec;
+        }
+    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 );
+                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( 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;
+        }
+        std::string subString() const { return m_arg.substr( m_start, m_pos - m_start ); }
+        template<typename T>
+        void addPattern() {
+            std::string token = subString();
+            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 );
+            }
+            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();
+            }
+        }
+    };
+    inline TestSpec parseTestSpec( std::string const& arg ) {
+        return TestSpecParser( ITagAliasRegistry::get() ).parse( arg ).testSpec();
+    }
+
+} // namespace Catch
+
+#ifdef __clang__
+#pragma clang diagnostic pop
+#endif
+
+// #included from: catch_interfaces_config.h
+#define TWOBLUECUBES_CATCH_INTERFACES_CONFIG_H_INCLUDED
+
+#include <iostream>
+#include <string>
+#include <vector>
+
+namespace Catch {
+
+    struct Verbosity { enum Level {
+        NoOutput = 0,
+        Quiet,
+        Normal
+    }; };
+
+    struct WarnAbout { enum What {
+        Nothing = 0x00,
+        NoAssertions = 0x01
+    }; };
+
+    struct ShowDurations { enum OrNot {
+        DefaultForReporter,
+        Always,
+        Never
+    }; };
+    struct RunTests { enum InWhatOrder {
+        InDeclarationOrder,
+        InLexicographicalOrder,
+        InRandomOrder
+    }; };
+
+    class TestSpec;
+
+    struct IConfig : IShared {
+
+        virtual ~IConfig();
+
+        virtual bool allowThrows() const = 0;
+        virtual std::ostream& stream() const = 0;
+        virtual std::string name() const = 0;
+        virtual bool includeSuccessfulResults() const = 0;
+        virtual bool shouldDebugBreak() const = 0;
+        virtual bool warnAboutMissingAssertions() const = 0;
+        virtual 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 bool forceColour() const = 0;
+    };
+}
+
+// #included from: catch_stream.h
+#define TWOBLUECUBES_CATCH_STREAM_H_INCLUDED
+
+#include <streambuf>
+
+#ifdef __clang__
+#pragma clang diagnostic ignored "-Wpadded"
+#endif
+
+namespace Catch {
+
+    class Stream {
+    public:
+        Stream();
+        Stream( std::streambuf* _streamBuf, bool _isOwned );
+        void release();
+
+        std::streambuf* streamBuf;
+
+    private:
+        bool isOwned;
+    };
+
+    std::ostream& cout();
+    std::ostream& cerr();
+}
+
+#include <memory>
+#include <vector>
+#include <string>
+#include <iostream>
+#include <ctime>
+
+#ifndef CATCH_CONFIG_CONSOLE_WIDTH
+#define CATCH_CONFIG_CONSOLE_WIDTH 80
+#endif
+
+namespace Catch {
+
+    struct ConfigData {
+
+        ConfigData()
+        :   listTests( false ),
+            listTags( false ),
+            listReporters( false ),
+            listTestNamesOnly( false ),
+            showSuccessfulTests( false ),
+            shouldDebugBreak( false ),
+            noThrow( false ),
+            showHelp( false ),
+            showInvisibles( false ),
+            forceColour( false ),
+            abortAfter( -1 ),
+            rngSeed( 0 ),
+            verbosity( Verbosity::Normal ),
+            warnings( WarnAbout::Nothing ),
+            showDurations( ShowDurations::DefaultForReporter ),
+            runOrder( RunTests::InDeclarationOrder )
+        {}
+
+        bool listTests;
+        bool listTags;
+        bool listReporters;
+        bool listTestNamesOnly;
+
+        bool showSuccessfulTests;
+        bool shouldDebugBreak;
+        bool noThrow;
+        bool showHelp;
+        bool showInvisibles;
+        bool forceColour;
+
+        int abortAfter;
+        unsigned int rngSeed;
+
+        Verbosity::Level verbosity;
+        WarnAbout::What warnings;
+        ShowDurations::OrNot showDurations;
+        RunTests::InWhatOrder runOrder;
+
+        std::string reporterName;
+        std::string outputFilename;
+        std::string name;
+        std::string processName;
+
+        std::vector<std::string> testsOrTags;
+    };
+
+    class Config : public SharedImpl<IConfig> {
+    private:
+        Config( Config const& other );
+        Config& operator = ( Config const& other );
+        virtual void dummy();
+    public:
+
+        Config()
+        :   m_os( Catch::cout().rdbuf() )
+        {}
+
+        Config( ConfigData const& data )
+        :   m_data( data ),
+            m_os( Catch::cout().rdbuf() )
+        {
+            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();
+            }
+        }
+
+        virtual ~Config() {
+            m_os.rdbuf( Catch::cout().rdbuf() );
+            m_stream.release();
+        }
+
+        void setFilename( std::string const& filename ) {
+            m_data.outputFilename = filename;
+        }
+
+        std::string const& getFilename() const {
+            return m_data.outputFilename ;
+        }
+
+        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; }
+
+        std::string getProcessName() const { return m_data.processName; }
+
+        bool shouldDebugBreak() const { return m_data.shouldDebugBreak; }
+
+        void setStreamBuf( std::streambuf* buf ) {
+            m_os.rdbuf( buf ? buf : Catch::cout().rdbuf() );
+        }
+
+        void useStream( std::string const& streamName ) {
+            Stream stream = createStream( streamName );
+            setStreamBuf( stream.streamBuf );
+            m_stream.release();
+            m_stream = stream;
+        }
+
+        std::string getReporterName() const { return m_data.reporterName; }
+
+        int abortAfter() const { return m_data.abortAfter; }
+
+        TestSpec const& testSpec() const { return m_testSpec; }
+
+        bool showHelp() const { return m_data.showHelp; }
+        bool showInvisibles() const { return m_data.showInvisibles; }
+
+        // IConfig interface
+        virtual bool allowThrows() const        { return !m_data.noThrow; }
+        virtual std::ostream& stream() const    { return m_os; }
+        virtual std::string name() const        { return m_data.name.empty() ? m_data.processName : m_data.name; }
+        virtual bool includeSuccessfulResults() const   { return m_data.showSuccessfulTests; }
+        virtual bool warnAboutMissingAssertions() const { return m_data.warnings & WarnAbout::NoAssertions; }
+        virtual ShowDurations::OrNot showDurations() const { return m_data.showDurations; }
+        virtual RunTests::InWhatOrder runOrder() const  { return m_data.runOrder; }
+        virtual unsigned int rngSeed() const    { return m_data.rngSeed; }
+        virtual bool forceColour() const { return m_data.forceColour; }
+
+    private:
+        ConfigData m_data;
+
+        Stream m_stream;
+        mutable std::ostream m_os;
+        TestSpec m_testSpec;
+    };
+
+} // 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
+
+// Only use header guard if we are not using an outer namespace
+#if !defined(TWOBLUECUBES_CLARA_H_INCLUDED) || defined(STITCH_CLARA_OPEN_NAMESPACE)
+
+#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
+
+#define STITCH_TBC_TEXT_FORMAT_OPEN_NAMESPACE STITCH_CLARA_OPEN_NAMESPACE
+
+// ----------- #included from tbc_text_format.h -----------
+
+// 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
+
+#include <string>
+#include <vector>
+#include <sstream>
+
+// Use optional outer namespace
+#ifdef STITCH_TBC_TEXT_FORMAT_OUTER_NAMESPACE
+namespace STITCH_TBC_TEXT_FORMAT_OUTER_NAMESPACE {
+#endif
+
+namespace Tbc {
+
+#ifdef TBC_TEXT_FORMAT_CONSOLE_WIDTH
+    const unsigned int consoleWidth = TBC_TEXT_FORMAT_CONSOLE_WIDTH;
+#else
+    const unsigned int consoleWidth = 80;
+#endif
+
+    struct TextAttributes {
+        TextAttributes()
+        :   initialIndent( std::string::npos ),
+            indent( 0 ),
+            width( consoleWidth-1 ),
+            tabChar( '\t' )
+        {}
+
+        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 );
+                }
+
+                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;
+                }
+            }
+        }
+
+        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 );
+        }
+
+        typedef std::vector<std::string>::const_iterator const_iterator;
+
+        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();
+        }
+
+        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;
+        }
+
+    private:
+        std::string str;
+        TextAttributes attr;
+        std::vector<std::string> lines;
+    };
+
+} // end namespace Tbc
+
+#ifdef STITCH_TBC_TEXT_FORMAT_OUTER_NAMESPACE
+} // end outer namespace
+#endif
+
+#endif // TBC_TEXT_FORMAT_H_INCLUDED
+
+// ----------- end of #include from tbc_text_format.h -----------
+// ........... back in /Users/philnash/Dev/OSS/Clara/srcs/clara.h
+
+#undef STITCH_TBC_TEXT_FORMAT_OPEN_NAMESPACE
+
+#include <map>
+#include <algorithm>
+#include <stdexcept>
+#include <memory>
+
+// Use optional outer namespace
+#ifdef STITCH_CLARA_OPEN_NAMESPACE
+STITCH_CLARA_OPEN_NAMESPACE
+#endif
+
+namespace Clara {
+
+    struct UnpositionalTag {};
+
+    extern UnpositionalTag _;
+
+#ifdef CLARA_CONFIG_MAIN
+    UnpositionalTag _;
+#endif
+
+    namespace Detail {
+
+#ifdef CLARA_CONSOLE_WIDTH
+    const unsigned int consoleWidth = CLARA_CONFIG_CONSOLE_WIDTH;
+#else
+    const unsigned int consoleWidth = 80;
+#endif
+
+        using namespace Tbc;
+
+        inline bool startsWith( std::string const& str, std::string const& prefix ) {
+            return str.size() >= prefix.size() && str.substr( 0, prefix.size() ) == prefix;
+        }
+
+        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; };
+
+        template<typename T>    struct IsBool       { static const bool value = false; };
+        template<>              struct IsBool<bool> { static const bool value = true; };
+
+        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;
+        }
+        inline void convertInto( std::string const& _source, bool& _dest ) {
+            std::string sourceLC = _source;
+            std::transform( sourceLC.begin(), sourceLC.end(), sourceLC.begin(), ::tolower );
+            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 + "'" );
+        }
+        inline void convertInto( bool _source, bool& _dest ) {
+            _dest = _source;
+        }
+        template<typename T>
+        inline void convertInto( bool, T& ) {
+            throw std::runtime_error( "Invalid conversion" );
+        }
+
+        template<typename ConfigT>
+        struct IArgFunction {
+            virtual ~IArgFunction() {}
+#  ifdef CATCH_CONFIG_CPP11_GENERATED_METHODS
+            IArgFunction()                      = default;
+            IArgFunction( IArgFunction const& ) = default;
+#  endif
+            virtual void set( ConfigT& config, std::string const& value ) const = 0;
+            virtual void setFlag( ConfigT& config ) const = 0;
+            virtual bool takesArg() const = 0;
+            virtual IArgFunction* clone() const = 0;
+        };
+
+        template<typename ConfigT>
+        class BoundArgFunction {
+        public:
+            BoundArgFunction() : functionObj( NULL ) {}
+            BoundArgFunction( IArgFunction<ConfigT>* _functionObj ) : functionObj( _functionObj ) {}
+            BoundArgFunction( BoundArgFunction const& other ) : functionObj( other.functionObj ? other.functionObj->clone() : NULL ) {}
+            BoundArgFunction& operator = ( BoundArgFunction const& other ) {
+                IArgFunction<ConfigT>* newFunctionObj = other.functionObj ? other.functionObj->clone() : NULL;
+                delete functionObj;
+                functionObj = newFunctionObj;
+                return *this;
+            }
+            ~BoundArgFunction() { delete functionObj; }
+
+            void set( ConfigT& config, std::string const& value ) const {
+                functionObj->set( config, value );
+            }
+            void setFlag( ConfigT& config ) const {
+                functionObj->setFlag( config );
+            }
+            bool takesArg() const { return functionObj->takesArg(); }
+
+            bool isSet() const {
+                return functionObj != NULL;
+            }
+        private:
+            IArgFunction<ConfigT>* functionObj;
+        };
+
+        template<typename C>
+        struct NullBinder : IArgFunction<C>{
+            virtual void set( C&, std::string const& ) const {}
+            virtual void setFlag( C& ) 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 void setFlag( C& p ) const {
+                convertInto( true, 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 void setFlag( C& p ) const {
+                typename RemoveConstRef<M>::type value;
+                convertInto( true, 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 void setFlag( C& p ) const {
+                (p.*member)();
+            }
+            virtual bool takesArg() const { return false; }
+            virtual IArgFunction<C>* clone() const { return new BoundNullaryMethod( *this ); }
+            void (C::*member)();
+        };
+
+        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 void setFlag( C& p ) const {
+                function( p );
+            }
+            virtual bool takesArg() const { return false; }
+            virtual IArgFunction<C>* clone() const { return new BoundUnaryFunction( *this ); }
+            void (*function)( C& );
+        };
+
+        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 void setFlag( C& obj ) const {
+                typename RemoveConstRef<T>::type value;
+                convertInto( true, 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 );
+        };
+
+    } // namespace Detail
+
+    struct Parser {
+        Parser() : separators( " \t=:" ) {}
+
+        struct Token {
+            enum Type { Positional, ShortOpt, LongOpt };
+            Token( Type _type, std::string const& _data ) : type( _type ), data( _data ) {}
+            Type type;
+            std::string data;
+        };
+
+        void parseIntoTokens( int argc, char const * const * argv, std::vector<Parser::Token>& tokens ) const {
+            const std::string doubleDash = "--";
+            for( int i = 1; i < argc && argv[i] != doubleDash; ++i )
+                parseIntoTokens( argv[i] , tokens);
+        }
+        void parseIntoTokens( std::string arg, std::vector<Parser::Token>& tokens ) const {
+            while( !arg.empty() ) {
+                Parser::Token token( Parser::Token::Positional, arg );
+                arg = "";
+                if( token.data[0] == '-' ) {
+                    if( token.data.size() > 1 && token.data[1] == '-' ) {
+                        token = Parser::Token( Parser::Token::LongOpt, token.data.substr( 2 ) );
+                    }
+                    else {
+                        token = Parser::Token( Parser::Token::ShortOpt, token.data.substr( 1 ) );
+                        if( token.data.size() > 1 && separators.find( token.data[1] ) == std::string::npos ) {
+                            arg = "-" + token.data.substr( 1 );
+                            token.data = token.data.substr( 0, 1 );
+                        }
+                    }
+                }
+                if( token.type != Parser::Token::Positional ) {
+                    std::size_t pos = token.data.find_first_of( separators );
+                    if( pos != std::string::npos ) {
+                        arg = token.data.substr( pos+1 );
+                        token.data = token.data.substr( 0, pos );
+                    }
+                }
+                tokens.push_back( token );
+            }
+        }
+        std::string separators;
+    };
+
+    template<typename ConfigT>
+    struct CommonArgProperties {
+        CommonArgProperties() {}
+        CommonArgProperties( Detail::BoundArgFunction<ConfigT> const& _boundField ) : boundField( _boundField ) {}
+
+        Detail::BoundArgFunction<ConfigT> boundField;
+        std::string description;
+        std::string detail;
+        std::string placeholder; // Only value if boundField takes an arg
+
+        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;
+
+        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)
+
+        bool isFixedPositional() const {
+            return position != -1;
+        }
+    };
+
+    template<typename ConfigT>
+    class CommandLine {
+
+        struct Arg : CommonArgProperties<ConfigT>, OptionArgProperties, PositionalArgProperties {
+            Arg() {}
+            Arg( Detail::BoundArgFunction<ConfigT> const& _boundField ) : CommonArgProperties<ConfigT>( _boundField ) {}
+
+            using CommonArgProperties<ConfigT>::placeholder; // !TBD
+
+            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();
+            }
+        };
+
+        // NOTE: std::auto_ptr is deprecated in c++11/c++0x
+#if defined(__cplusplus) && __cplusplus > 199711L
+        typedef std::unique_ptr<Arg> ArgAutoPtr;
+#else
+        typedef std::auto_ptr<Arg> ArgAutoPtr;
+#endif
+
+        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 ArgBuilder {
+        public:
+            ArgBuilder( Arg* arg ) : m_arg( arg ) {}
+
+            // 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 );
+            }
+
+            // 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;
+            }
+
+            // 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 );
+            }
+
+            // 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 );
+            }
+
+            // 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 );
+            }
+
+            // 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;
+            }
+
+            ArgBuilder& describe( std::string const& description ) {
+                m_arg->description = description;
+                return *this;
+            }
+            ArgBuilder& detail( std::string const& detail ) {
+                m_arg->detail = detail;
+                return *this;
+            }
+
+        protected:
+            Arg* m_arg;
+        };
+
+        class OptBuilder : public ArgBuilder {
+        public:
+            OptBuilder( Arg* arg ) : ArgBuilder( arg ) {}
+            OptBuilder( OptBuilder& other ) : ArgBuilder( other ) {}
+
+            OptBuilder& operator[]( std::string const& optName ) {
+                addOptName( *ArgBuilder::m_arg, optName );
+                return *this;
+            }
+        };
+
+    public:
+
+        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 ) );
+        }
+
+        CommandLine& setThrowOnUnrecognisedTokens( bool shouldThrow = true ) {
+            m_throwOnUnrecognisedTokens = shouldThrow;
+            return *this;
+        }
+
+        OptBuilder operator[]( std::string const& optName ) {
+            m_options.push_back( Arg() );
+            addOptName( m_options.back(), optName );
+            OptBuilder builder( &m_options.back() );
+            return builder;
+        }
+
+        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;
+        }
+
+        // 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;
+        }
+
+        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 );
+        }
+
+        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() );
+
+            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 ) );
+
+                for( std::size_t i = 0; i < (std::max)( usage.size(), desc.size() ); ++i ) {
+                    std::string usageCol = i < usage.size() ? usage[i] : "";
+                    os << usageCol;
+
+                    if( i < desc.size() && !desc[i].empty() )
+                        os  << std::string( indent + 2 + maxWidth - usageCol.size(), ' ' )
+                            << desc[i];
+                    os << "\n";
+                }
+            }
+        }
+        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( int argc, char const * const * argv ) const {
+            ConfigT config;
+            parseInto( argc, argv, config );
+            return config;
+        }
+
+        std::vector<Parser::Token> parseInto( int argc, char const * const * argv, ConfigT& config ) const {
+            std::string processName = argv[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( argc, argv, 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.setFlag( config );
+                            }
+                            break;
+                        }
+                    }
+                    catch( std::exception& ex ) {
+                        errors.push_back( std::string( ex.what() ) + "\n- while parsing: (" + arg.commands() + ")" );
+                    }
+                }
+                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 );
+                }
+            }
+            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;
+                }
+                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" );
+
+            for( typename std::vector<Arg>::const_iterator  it = m_options.begin(),
+                                                            itEnd = m_options.end();
+                    it != itEnd; ++it )
+                it->validate();
+        }
+
+    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;
+    };
+
+} // end namespace Clara
+
+STITCH_CLARA_CLOSE_NAMESPACE
+#undef STITCH_CLARA_OPEN_NAMESPACE
+#undef STITCH_CLARA_CLOSE_NAMESPACE
+
+#endif // TWOBLUECUBES_CLARA_H_INCLUDED
+#undef STITCH_CLARA_OPEN_NAMESPACE
+
+// 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
+
+#include <fstream>
+
+namespace Catch {
+
+    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 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( "Argment 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 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 );
+
+        std::string line;
+        while( std::getline( f, line ) ) {
+            line = trim(line);
+            if( !line.empty() && !startsWith( line, "#" ) )
+                addTestOrTags( config, "\"" + line + "\"," );
+        }
+    }
+
+    inline Clara::CommandLine<ConfigData> makeCommandLineParser() {
+
+        using namespace Clara;
+        CommandLine<ConfigData> cli;
+
+        cli.bindProcessName( &ConfigData::processName );
+
+        cli["-?"]["-h"]["--help"]
+            .describe( "display usage information" )
+            .bind( &ConfigData::showHelp );
+
+        cli["-l"]["--list-tests"]
+            .describe( "list all/matching test cases" )
+            .bind( &ConfigData::listTests );
+
+        cli["-t"]["--list-tags"]
+            .describe( "list all/matching tags" )
+            .bind( &ConfigData::listTags );
+
+        cli["-s"]["--success"]
+            .describe( "include successful tests in output" )
+            .bind( &ConfigData::showSuccessfulTests );
+
+        cli["-b"]["--break"]
+            .describe( "break into debugger on failure" )
+            .bind( &ConfigData::shouldDebugBreak );
+
+        cli["-e"]["--nothrow"]
+            .describe( "skip exception tests" )
+            .bind( &ConfigData::noThrow );
+
+        cli["-i"]["--invisibles"]
+            .describe( "show invisibles (tabs, newlines)" )
+            .bind( &ConfigData::showInvisibles );
+
+        cli["-o"]["--out"]
+            .describe( "output filename" )
+            .bind( &ConfigData::outputFilename, "filename" );
+
+        cli["-r"]["--reporter"]
+//            .placeholder( "name[:filename]" )
+            .describe( "reporter to use (defaults to console)" )
+            .bind( &ConfigData::reporterName, "name" );
+
+        cli["-n"]["--name"]
+            .describe( "suite name" )
+            .bind( &ConfigData::name, "name" );
+
+        cli["-a"]["--abort"]
+            .describe( "abort at first failure" )
+            .bind( &abortAfterFirst );
+
+        cli["-x"]["--abortx"]
+            .describe( "abort after x failures" )
+            .bind( &abortAfterX, "no. failures" );
+
+        cli["-w"]["--warn"]
+            .describe( "enable warnings" )
+            .bind( &addWarning, "warning name" );
+
+// - needs updating if reinstated
+//        cli.into( &setVerbosity )
+//            .describe( "level of verbosity (0=no output)" )
+//            .shortOpt( "v")
+//            .longOpt( "verbosity" )
+//            .placeholder( "level" );
+
+        cli[_]
+            .describe( "which test or tests to use" )
+            .bind( &addTestOrTags, "test name, pattern or tags" );
+
+        cli["-d"]["--durations"]
+            .describe( "show test durations" )
+            .bind( &setShowDurations, "yes/no" );
+
+        cli["-f"]["--input-file"]
+            .describe( "load test names to run from a file" )
+            .bind( &loadTestNamesFromFile, "filename" );
+
+        // 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 );
+
+        cli["--list-reporters"]
+            .describe( "list all reporters" )
+            .bind( &ConfigData::listReporters );
+
+        cli["--order"]
+            .describe( "test case order (defaults to decl)" )
+            .bind( &setOrder, "decl|lex|rand" );
+
+        cli["--rng-seed"]
+            .describe( "set a specific seed for random numbers" )
+            .bind( &setRngSeed, "'time'|number" );
+
+        cli["--force-colour"]
+            .describe( "force colourised output" )
+            .bind( &ConfigData::forceColour );
+
+        return cli;
+    }
+
+} // end namespace Catch
+
+// #included from: internal/catch_list.hpp
+#define TWOBLUECUBES_CATCH_LIST_HPP_INCLUDED
+
+// #included from: catch_text.h
+#define TWOBLUECUBES_CATCH_TEXT_H_INCLUDED
+
+#define TBC_TEXT_FORMAT_CONSOLE_WIDTH CATCH_CONFIG_CONSOLE_WIDTH
+
+#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
+#endif
+#ifndef TWOBLUECUBES_TEXT_FORMAT_H_ALREADY_INCLUDED
+#include <string>
+#include <vector>
+#include <sstream>
+
+// Use optional outer namespace
+#ifdef CLICHE_TBC_TEXT_FORMAT_OUTER_NAMESPACE
+namespace CLICHE_TBC_TEXT_FORMAT_OUTER_NAMESPACE {
+#endif
+
+namespace Tbc {
+
+#ifdef TBC_TEXT_FORMAT_CONSOLE_WIDTH
+    const unsigned int consoleWidth = TBC_TEXT_FORMAT_CONSOLE_WIDTH;
+#else
+    const unsigned int consoleWidth = 80;
+#endif
+
+    struct TextAttributes {
+        TextAttributes()
+        :   initialIndent( std::string::npos ),
+            indent( 0 ),
+            width( consoleWidth-1 ),
+            tabChar( '\t' )
+        {}
+
+        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 );
+                }
+
+                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;
+                }
+            }
+        }
+
+        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 );
+        }
+
+        typedef std::vector<std::string>::const_iterator const_iterator;
+
+        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();
+        }
+
+        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;
+        }
+
+    private:
+        std::string str;
+        TextAttributes attr;
+        std::vector<std::string> lines;
+    };
+
+} // end namespace Tbc
+
+#ifdef CLICHE_TBC_TEXT_FORMAT_OUTER_NAMESPACE
+} // end outer namespace
+#endif
+
+#endif // TWOBLUECUBES_TEXT_FORMAT_H_ALREADY_INCLUDED
+#undef CLICHE_TBC_TEXT_FORMAT_OUTER_NAMESPACE
+
+namespace Catch {
+    using Tbc::Text;
+    using Tbc::TextAttributes;
+}
+
+// #included from: catch_console_colour.hpp
+#define TWOBLUECUBES_CATCH_CONSOLE_COLOUR_HPP_INCLUDED
+
+namespace Catch {
+
+    struct Colour {
+        enum Code {
+            None = 0,
+
+            White,
+            Red,
+            Green,
+            Blue,
+            Cyan,
+            Yellow,
+            Grey,
+
+            Bright = 0x10,
+
+            BrightRed = Bright | Red,
+            BrightGreen = Bright | Green,
+            LightGrey = Bright | Grey,
+            BrightWhite = Bright | White,
+
+            // By intention
+            FileName = LightGrey,
+            Warning = Yellow,
+            ResultError = BrightRed,
+            ResultSuccess = BrightGreen,
+            ResultExpectedFailure = Warning,
+
+            Error = BrightRed,
+            Success = Green,
+
+            OriginalExpression = Cyan,
+            ReconstructedExpression = Yellow,
+
+            SecondaryText = LightGrey,
+            Headers = White
+        };
+
+        // 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 );
+
+    private:
+        bool m_moved;
+    };
+
+    inline std::ostream& operator << ( std::ostream& os, Colour const& ) { return os; }
+
+} // end namespace Catch
+
+// #included from: catch_interfaces_reporter.h
+#define TWOBLUECUBES_CATCH_INTERFACES_REPORTER_H_INCLUDED
+
+#include <string>
+#include <ostream>
+#include <map>
+#include <assert.h>
+
+namespace Catch
+{
+    struct ReporterConfig {
+        explicit ReporterConfig( Ptr<IConfig> const& _fullConfig )
+        :   m_stream( &_fullConfig->stream() ), m_fullConfig( _fullConfig ) {}
+
+        ReporterConfig( Ptr<IConfig> const& _fullConfig, std::ostream& _stream )
+        :   m_stream( &_stream ), m_fullConfig( _fullConfig ) {}
+
+        std::ostream& stream() const    { return *m_stream; }
+        Ptr<IConfig> fullConfig() const { return m_fullConfig; }
+
+    private:
+        std::ostream* m_stream;
+        Ptr<IConfig> m_fullConfig;
+    };
+
+    struct ReporterPreferences {
+        ReporterPreferences()
+        : shouldRedirectStdOut( false )
+        {}
+
+        bool shouldRedirectStdOut;
+    };
+
+    template<typename T>
+    struct LazyStat : Option<T> {
+        LazyStat() : used( false ) {}
+        LazyStat& operator=( T const& _value ) {
+            Option<T>::operator=( _value );
+            used = false;
+            return *this;
+        }
+        void reset() {
+            Option<T>::reset();
+            used = false;
+        }
+        bool used;
+    };
+
+    struct TestRunInfo {
+        TestRunInfo( std::string const& _name ) : name( _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::string name;
+        std::size_t groupIndex;
+        std::size_t groupsCounts;
+    };
+
+    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();
+
+#  ifdef CATCH_CONFIG_CPP11_GENERATED_METHODS
+        AssertionStats( AssertionStats const& )              = default;
+        AssertionStats( AssertionStats && )                  = default;
+        AssertionStats& operator = ( AssertionStats const& ) = default;
+        AssertionStats& operator = ( AssertionStats && )     = default;
+#  endif
+
+        AssertionResult assertionResult;
+        std::vector<MessageInfo> infoMessages;
+        Totals totals;
+    };
+
+    struct SectionStats {
+        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
+        SectionStats( SectionStats const& )              = default;
+        SectionStats( SectionStats && )                  = default;
+        SectionStats& operator = ( SectionStats const& ) = default;
+        SectionStats& operator = ( SectionStats && )     = default;
+#  endif
+
+        SectionInfo sectionInfo;
+        Counts assertions;
+        double durationInSeconds;
+        bool missingAssertions;
+    };
+
+    struct 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 )
+        {}
+        virtual ~TestCaseStats();
+
+#  ifdef CATCH_CONFIG_CPP11_GENERATED_METHODS
+        TestCaseStats( TestCaseStats const& )              = default;
+        TestCaseStats( TestCaseStats && )                  = default;
+        TestCaseStats& operator = ( TestCaseStats const& ) = default;
+        TestCaseStats& operator = ( TestCaseStats && )     = default;
+#  endif
+
+        TestCaseInfo testInfo;
+        Totals totals;
+        std::string stdOut;
+        std::string stdErr;
+        bool aborting;
+    };
+
+    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();
+
+#  ifdef CATCH_CONFIG_CPP11_GENERATED_METHODS
+        TestGroupStats( TestGroupStats const& )              = default;
+        TestGroupStats( TestGroupStats && )                  = default;
+        TestGroupStats& operator = ( TestGroupStats const& ) = default;
+        TestGroupStats& operator = ( TestGroupStats && )     = default;
+#  endif
+
+        GroupInfo groupInfo;
+        Totals totals;
+        bool aborting;
+    };
+
+    struct TestRunStats {
+        TestRunStats(   TestRunInfo const& _runInfo,
+                        Totals const& _totals,
+                        bool _aborting )
+        :   runInfo( _runInfo ),
+            totals( _totals ),
+            aborting( _aborting )
+        {}
+        virtual ~TestRunStats();
+
+#  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
+
+        TestRunInfo runInfo;
+        Totals totals;
+        bool aborting;
+    };
+
+    struct IStreamingReporter : IShared {
+        virtual ~IStreamingReporter();
+
+        // Implementing class must also provide the following static method:
+        // static std::string getDescription();
+
+        virtual ReporterPreferences getPreferences() const = 0;
+
+        virtual void noMatchingTestCases( std::string const& spec ) = 0;
+
+        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;
+
+        virtual void assertionStarting( AssertionInfo const& assertionInfo ) = 0;
+
+        // The return value indicates if the messages buffer should be cleared:
+        virtual bool assertionEnded( AssertionStats const& assertionStats ) = 0;
+        virtual void sectionEnded( SectionStats const& sectionStats ) = 0;
+        virtual void testCaseEnded( TestCaseStats const& testCaseStats ) = 0;
+        virtual void testGroupEnded( TestGroupStats const& testGroupStats ) = 0;
+        virtual void testRunEnded( TestRunStats const& testRunStats ) = 0;
+
+        virtual void skipTest( TestCaseInfo const& testInfo ) = 0;
+    };
+
+    struct IReporterFactory {
+        virtual ~IReporterFactory();
+        virtual IStreamingReporter* create( ReporterConfig const& config ) const = 0;
+        virtual std::string getDescription() const = 0;
+    };
+
+    struct IReporterRegistry {
+        typedef std::map<std::string, IReporterFactory*> FactoryMap;
+
+        virtual ~IReporterRegistry();
+        virtual IStreamingReporter* create( std::string const& name, Ptr<IConfig> const& config ) const = 0;
+        virtual FactoryMap const& getFactories() const = 0;
+    };
+
+}
+
+#include <limits>
+#include <algorithm>
+
+namespace Catch {
+
+    inline std::size_t listTests( Config const& config ) {
+
+        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();
+        }
+
+        std::size_t matchedTests = 0;
+        TextAttributes nameAttr, tagsAttr;
+        nameAttr.setInitialIndent( 2 ).setIndent( 4 );
+        tagsAttr.setIndent( 6 );
+
+        std::vector<TestCase> matchedTestCases;
+        getRegistryHub().getTestCaseRegistry().getFilteredTests( testSpec, config, matchedTestCases );
+        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 );
+
+            Catch::cout() << Text( testCaseInfo.name, nameAttr ) << std::endl;
+            if( !testCaseInfo.tags.empty() )
+                Catch::cout() << Text( testCaseInfo.tagsAsString, tagsAttr ) << std::endl;
+        }
+
+        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;
+        getRegistryHub().getTestCaseRegistry().getFilteredTests( testSpec, config, matchedTestCases );
+        for( std::vector<TestCase>::const_iterator it = matchedTestCases.begin(), itEnd = matchedTestCases.end();
+                it != itEnd;
+                ++it ) {
+            matchedTests++;
+            TestCaseInfo const& testCaseInfo = it->getTestCaseInfo();
+            Catch::cout() << testCaseInfo.name << std::endl;
+        }
+        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;
+        }
+        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();
+        }
+
+        std::map<std::string, TagInfo> tagCounts;
+
+        std::vector<TestCase> matchedTestCases;
+        getRegistryHub().getTestCaseRegistry().getFilteredTests( testSpec, config, matchedTestCases );
+        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 );
+            }
+        }
+
+        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";
+        }
+        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";
+        }
+        Catch::cout() << std::endl;
+        return factories.size();
+    }
+
+    inline Option<std::size_t> list( Config const& config ) {
+        Option<std::size_t> listedCount;
+        if( config.listTests() )
+            listedCount = listedCount.valueOr(0) + listTests( config );
+        if( config.listTestNamesOnly() )
+            listedCount = listedCount.valueOr(0) + listTestsNamesOnly( config );
+        if( config.listTags() )
+            listedCount = listedCount.valueOr(0) + listTags( config );
+        if( config.listReporters() )
+            listedCount = listedCount.valueOr(0) + listReporters( config );
+        return listedCount;
+    }
+
+} // end namespace Catch
+
+// #included from: internal/catch_runner_impl.hpp
+#define TWOBLUECUBES_CATCH_RUNNER_IMPL_HPP_INCLUDED
+
+// #included from: catch_test_case_tracker.hpp
+#define TWOBLUECUBES_CATCH_TEST_CASE_TRACKER_HPP_INCLUDED
+
+#include <map>
+#include <string>
+#include <assert.h>
+
+namespace Catch {
+namespace SectionTracking {
+
+    class TrackedSection {
+
+        typedef std::map<std::string, TrackedSection> TrackedSections;
+
+    public:
+        enum RunState {
+            NotStarted,
+            Executing,
+            ExecutingChildren,
+            Completed
+        };
+
+        TrackedSection( std::string const& name, TrackedSection* parent )
+        :   m_name( name ), m_runState( NotStarted ), m_parent( parent )
+        {}
+
+        RunState runState() const { return m_runState; }
+
+        TrackedSection* findChild( std::string const& childName );
+        TrackedSection* acquireChild( std::string const& childName );
+
+        void enter() {
+            if( m_runState == NotStarted )
+                m_runState = Executing;
+        }
+        void leave();
+
+        TrackedSection* getParent() {
+            return m_parent;
+        }
+        bool hasChildren() const {
+            return !m_children.empty();
+        }
+
+    private:
+        std::string m_name;
+        RunState m_runState;
+        TrackedSections m_children;
+        TrackedSection* m_parent;
+    };
+
+    inline TrackedSection* TrackedSection::findChild( std::string const& childName ) {
+        TrackedSections::iterator it = m_children.find( childName );
+        return it != m_children.end()
+            ? &it->second
+            : NULL;
+    }
+    inline TrackedSection* TrackedSection::acquireChild( std::string const& childName ) {
+        if( TrackedSection* child = findChild( childName ) )
+            return child;
+        m_children.insert( std::make_pair( childName, TrackedSection( childName, this ) ) );
+        return findChild( childName );
+    }
+    inline void TrackedSection::leave() {
+        for( TrackedSections::const_iterator it = m_children.begin(), itEnd = m_children.end();
+                it != itEnd;
+                ++it )
+            if( it->second.runState() != Completed ) {
+                m_runState = ExecutingChildren;
+                return;
+            }
+        m_runState = Completed;
+    }
+
+    class TestCaseTracker {
+    public:
+        TestCaseTracker( std::string const& testCaseName )
+        :   m_testCase( testCaseName, NULL ),
+            m_currentSection( &m_testCase ),
+            m_completedASectionThisRun( false )
+        {}
+
+        bool enterSection( std::string const& name ) {
+            TrackedSection* child = m_currentSection->acquireChild( name );
+            if( m_completedASectionThisRun || child->runState() == TrackedSection::Completed )
+                return false;
+
+            m_currentSection = child;
+            m_currentSection->enter();
+            return true;
+        }
+        void leaveSection() {
+            m_currentSection->leave();
+            m_currentSection = m_currentSection->getParent();
+            assert( m_currentSection != NULL );
+            m_completedASectionThisRun = true;
+        }
+
+        bool currentSectionHasChildren() const {
+            return m_currentSection->hasChildren();
+        }
+        bool isCompleted() const {
+            return m_testCase.runState() == TrackedSection::Completed;
+        }
+
+        class Guard {
+        public:
+            Guard( TestCaseTracker& tracker ) : m_tracker( tracker ) {
+                m_tracker.enterTestCase();
+            }
+            ~Guard() {
+                m_tracker.leaveTestCase();
+            }
+        private:
+            Guard( Guard const& );
+            void operator = ( Guard const& );
+            TestCaseTracker& m_tracker;
+        };
+
+    private:
+        void enterTestCase() {
+            m_currentSection = &m_testCase;
+            m_completedASectionThisRun = false;
+            m_testCase.enter();
+        }
+        void leaveTestCase() {
+            m_testCase.leave();
+        }
+
+        TrackedSection m_testCase;
+        TrackedSection* m_currentSection;
+        bool m_completedASectionThisRun;
+    };
+
+} // namespace SectionTracking
+
+using SectionTracking::TestCaseTracker;
+
+} // namespace Catch
+
+// #included from: catch_fatal_condition.hpp
+#define TWOBLUECUBES_CATCH_FATAL_CONDITION_H_INCLUDED
+
+namespace Catch {
+
+    // Report the error condition then exit the process
+    inline void fatal( std::string const& message, int exitCode ) {
+        IContext& context = Catch::getCurrentContext();
+        IResultCapture* resultCapture = context.getResultCapture();
+        resultCapture->handleFatalErrorCondition( message );
+
+		if( Catch::alwaysTrue() ) // avoids "no return" warnings
+            exit( exitCode );
+    }
+
+} // namespace Catch
+
+#if defined ( CATCH_PLATFORM_WINDOWS ) /////////////////////////////////////////
+
+namespace Catch {
+
+    struct FatalConditionHandler {
+		void reset() {}
+	};
+
+} // namespace Catch
+
+#else // Not Windows - assumed to be POSIX compatible //////////////////////////
+
+#include <signal.h>
+
+namespace Catch {
+
+    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 void handleSignal( int sig ) {
+            for( std::size_t i = 0; i < sizeof(signalDefs)/sizeof(SignalDefs); ++i )
+                if( sig == signalDefs[i].id )
+                    fatal( signalDefs[i].name, -sig );
+            fatal( "<unknown signal>", -sig );
+        }
+
+        FatalConditionHandler() : m_isSet( true ) {
+            for( std::size_t i = 0; i < sizeof(signalDefs)/sizeof(SignalDefs); ++i )
+                signal( signalDefs[i].id, handleSignal );
+        }
+        ~FatalConditionHandler() {
+            reset();
+        }
+        void reset() {
+            if( m_isSet ) {
+                for( std::size_t i = 0; i < sizeof(signalDefs)/sizeof(SignalDefs); ++i )
+                    signal( signalDefs[i].id, SIG_DFL );
+                m_isSet = false;
+            }
+        }
+
+        bool m_isSet;
+    };
+
+} // namespace Catch
+
+#endif // not Windows
+
+#include <set>
+#include <string>
+
+namespace Catch {
+
+    class StreamRedirect {
+
+    public:
+        StreamRedirect( std::ostream& stream, std::string& targetString )
+        :   m_stream( stream ),
+            m_prevBuf( stream.rdbuf() ),
+            m_targetString( targetString )
+        {
+            stream.rdbuf( m_oss.rdbuf() );
+        }
+
+        ~StreamRedirect() {
+            m_targetString += m_oss.str();
+            m_stream.rdbuf( m_prevBuf );
+        }
+
+    private:
+        std::ostream& m_stream;
+        std::streambuf* m_prevBuf;
+        std::ostringstream m_oss;
+        std::string& m_targetString;
+    };
+
+    ///////////////////////////////////////////////////////////////////////////
+
+    class RunContext : public IResultCapture, public IRunner {
+
+        RunContext( RunContext const& );
+        void operator =( RunContext const& );
+
+    public:
+
+        explicit RunContext( Ptr<IConfig const> const& config, Ptr<IStreamingReporter> const& reporter )
+        :   m_runInfo( config->name() ),
+            m_context( getCurrentMutableContext() ),
+            m_activeTestCase( NULL ),
+            m_config( config ),
+            m_reporter( reporter ),
+            m_prevRunner( m_context.getRunner() ),
+            m_prevResultCapture( m_context.getResultCapture() ),
+            m_prevConfig( m_context.getConfig() )
+        {
+            m_context.setRunner( this );
+            m_context.setConfig( m_config );
+            m_context.setResultCapture( this );
+            m_reporter->testRunStarting( m_runInfo );
+        }
+
+        virtual ~RunContext() {
+            m_reporter->testRunEnded( TestRunStats( m_runInfo, m_totals, aborting() ) );
+            m_context.setRunner( m_prevRunner );
+            m_context.setConfig( NULL );
+            m_context.setResultCapture( m_prevResultCapture );
+            m_context.setConfig( m_prevConfig );
+        }
+
+        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() ) );
+        }
+
+        Totals runTest( TestCase const& testCase ) {
+            Totals prevTotals = m_totals;
+
+            std::string redirectedCout;
+            std::string redirectedCerr;
+
+            TestCaseInfo testInfo = testCase.getTestCaseInfo();
+
+            m_reporter->testCaseStarting( testInfo );
+
+            m_activeTestCase = &testCase;
+            m_testCaseTracker = TestCaseTracker( testInfo.name );
+
+            do {
+                do {
+                    runCurrentTest( redirectedCout, redirectedCerr );
+                }
+                while( !m_testCaseTracker->isCompleted() && !aborting() );
+            }
+            while( getCurrentContext().advanceGeneratorsForCurrentTest() && !aborting() );
+
+            Totals deltaTotals = m_totals.delta( prevTotals );
+            m_totals.testCases += deltaTotals.testCases;
+            m_reporter->testCaseEnded( TestCaseStats(   testInfo,
+                                                        deltaTotals,
+                                                        redirectedCout,
+                                                        redirectedCerr,
+                                                        aborting() ) );
+
+            m_activeTestCase = NULL;
+            m_testCaseTracker.reset();
+
+            return deltaTotals;
+        }
+
+        Ptr<IConfig const> config() const {
+            return m_config;
+        }
+
+    private: // IResultCapture
+
+        virtual void assertionEnded( AssertionResult const& result ) {
+            if( result.getResultType() == ResultWas::Ok ) {
+                m_totals.assertions.passed++;
+            }
+            else if( !result.isOk() ) {
+                m_totals.assertions.failed++;
+            }
+
+            if( m_reporter->assertionEnded( AssertionStats( result, m_messages, m_totals ) ) )
+                m_messages.clear();
+
+            // Reset working state
+            m_lastAssertionInfo = AssertionInfo( "", m_lastAssertionInfo.lineInfo, "{Unknown expression after the reported line}" , m_lastAssertionInfo.resultDisposition );
+            m_lastResult = result;
+        }
+
+        virtual bool sectionStarted (
+            SectionInfo const& sectionInfo,
+            Counts& assertions
+        )
+        {
+            std::ostringstream oss;
+            oss << sectionInfo.name << "@" << sectionInfo.lineInfo;
+
+            if( !m_testCaseTracker->enterSection( oss.str() ) )
+                return false;
+
+            m_lastAssertionInfo.lineInfo = sectionInfo.lineInfo;
+
+            m_reporter->sectionStarting( sectionInfo );
+
+            assertions = m_totals.assertions;
+
+            return true;
+        }
+        bool testForMissingAssertions( Counts& assertions ) {
+            if( assertions.total() != 0 ||
+                    !m_config->warnAboutMissingAssertions() ||
+                    m_testCaseTracker->currentSectionHasChildren() )
+                return false;
+            m_totals.assertions.failed++;
+            assertions.failed++;
+            return true;
+        }
+
+        virtual void sectionEnded( SectionInfo const& info, Counts const& prevAssertions, double _durationInSeconds ) {
+            if( std::uncaught_exception() ) {
+                m_unfinishedSections.push_back( UnfinishedSections( info, prevAssertions, _durationInSeconds ) );
+                return;
+            }
+
+            Counts assertions = m_totals.assertions - prevAssertions;
+            bool missingAssertions = testForMissingAssertions( assertions );
+
+            m_testCaseTracker->leaveSection();
+
+            m_reporter->sectionEnded( SectionStats( info, assertions, _durationInSeconds, missingAssertions ) );
+            m_messages.clear();
+        }
+
+        virtual void pushScopedMessage( MessageInfo const& message ) {
+            m_messages.push_back( message );
+        }
+
+        virtual void popScopedMessage( MessageInfo const& message ) {
+            m_messages.erase( std::remove( m_messages.begin(), m_messages.end(), message ), m_messages.end() );
+        }
+
+        virtual std::string getCurrentTestName() const {
+            return m_activeTestCase
+                ? m_activeTestCase->getTestCaseInfo().name
+                : "";
+        }
+
+        virtual const AssertionResult* getLastResult() const {
+            return &m_lastResult;
+        }
+
+        virtual void handleFatalErrorCondition( std::string const& message ) {
+            ResultBuilder resultBuilder = makeUnexpectedResultBuilder();
+            resultBuilder.setResultType( ResultWas::FatalErrorCondition );
+            resultBuilder << message;
+            resultBuilder.captureExpression();
+
+            handleUnfinishedSections();
+
+            // 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 );
+
+            Counts assertions;
+            assertions.failed = 1;
+            SectionStats testCaseSectionStats( testCaseSection, assertions, 0, false );
+            m_reporter->sectionEnded( testCaseSectionStats );
+
+            TestCaseInfo testInfo = m_activeTestCase->getTestCaseInfo();
+
+            Totals deltaTotals;
+            deltaTotals.testCases.failed = 1;
+            m_reporter->testCaseEnded( TestCaseStats(   testInfo,
+                                                        deltaTotals,
+                                                        "",
+                                                        "",
+                                                        false ) );
+            m_totals.testCases.failed++;
+            testGroupEnded( "", m_totals, 1, 1 );
+            m_reporter->testRunEnded( TestRunStats( m_runInfo, m_totals, false ) );
+        }
+
+    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() );
+        }
+
+    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;
+            try {
+                m_lastAssertionInfo = AssertionInfo( "TEST_CASE", testCaseInfo.lineInfo, "", ResultDisposition::Normal );
+                TestCaseTracker::Guard guard( *m_testCaseTracker );
+
+                Timer timer;
+                timer.start();
+                if( m_reporter->getPreferences().shouldRedirectStdOut ) {
+                    StreamRedirect coutRedir( Catch::cout(), redirectedCout );
+                    StreamRedirect cerrRedir( Catch::cerr(), redirectedCerr );
+                    invokeActiveTestCase();
+                }
+                else {
+                    invokeActiveTestCase();
+                }
+                duration = timer.getElapsedSeconds();
+            }
+            catch( TestFailureException& ) {
+                // This just means the test was aborted due to failure
+            }
+            catch(...) {
+                makeUnexpectedResultBuilder().useActiveException();
+            }
+            handleUnfinishedSections();
+            m_messages.clear();
+
+            Counts assertions = m_totals.assertions - prevAssertions;
+            bool missingAssertions = testForMissingAssertions( assertions );
+
+            if( testCaseInfo.okToFail() ) {
+                std::swap( assertions.failedButOk, assertions.failed );
+                m_totals.assertions.failed -= assertions.failedButOk;
+                m_totals.assertions.failedButOk += assertions.failedButOk;
+            }
+
+            SectionStats testCaseSectionStats( testCaseSection, assertions, duration, missingAssertions );
+            m_reporter->sectionEnded( testCaseSectionStats );
+        }
+
+        void invokeActiveTestCase() {
+            FatalConditionHandler fatalConditionHandler; // Handle signals
+            m_activeTestCase->invoke();
+            fatalConditionHandler.reset();
+        }
+
+    private:
+
+        ResultBuilder makeUnexpectedResultBuilder() const {
+            return ResultBuilder(   m_lastAssertionInfo.macroName.c_str(),
+                                    m_lastAssertionInfo.lineInfo,
+                                    m_lastAssertionInfo.capturedExpression.c_str(),
+                                    m_lastAssertionInfo.resultDisposition );
+        }
+
+        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<UnfinishedSections>::const_reverse_iterator it = m_unfinishedSections.rbegin(),
+                        itEnd = m_unfinishedSections.rend();
+                    it != itEnd;
+                    ++it )
+                sectionEnded( it->info, it->prevAssertions, it->durationInSeconds );
+            m_unfinishedSections.clear();
+        }
+
+        struct UnfinishedSections {
+            UnfinishedSections( SectionInfo const& _info, Counts const& _prevAssertions, double _durationInSeconds )
+            : info( _info ), prevAssertions( _prevAssertions ), durationInSeconds( _durationInSeconds )
+            {}
+
+            SectionInfo info;
+            Counts prevAssertions;
+            double durationInSeconds;
+        };
+
+        TestRunInfo m_runInfo;
+        IMutableContext& m_context;
+        TestCase const* m_activeTestCase;
+        Option<TestCaseTracker> m_testCaseTracker;
+        AssertionResult m_lastResult;
+
+        Ptr<IConfig const> m_config;
+        Totals m_totals;
+        Ptr<IStreamingReporter> m_reporter;
+        std::vector<MessageInfo> m_messages;
+        IRunner* m_prevRunner;
+        IResultCapture* m_prevResultCapture;
+        Ptr<IConfig const> m_prevConfig;
+        AssertionInfo m_lastAssertionInfo;
+        std::vector<UnfinishedSections> m_unfinishedSections;
+    };
+
+    IResultCapture& getResultCapture() {
+        if( IResultCapture* capture = getCurrentContext().getResultCapture() )
+            return *capture;
+        else
+            throw std::logic_error( "No result capture instance" );
+    }
+
+} // end namespace Catch
+
+// #included from: internal/catch_version.h
+#define TWOBLUECUBES_CATCH_VERSION_H_INCLUDED
+
+namespace Catch {
+
+    // Versioning information
+    struct Version {
+        Version(    unsigned int _majorVersion,
+                    unsigned int _minorVersion,
+                    unsigned int _patchNumber,
+                    std::string 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
+        std::string const branchName;
+        unsigned int const buildNumber;
+
+        friend std::ostream& operator << ( std::ostream& os, Version const& version );
+
+    private:
+        void operator=( Version const& );
+    };
+
+    extern Version libraryVersion;
+}
+
+#include <fstream>
+#include <stdlib.h>
+#include <limits>
+
+namespace Catch {
+
+    class Runner {
+
+    public:
+        Runner( Ptr<Config> const& config )
+        :   m_config( config )
+        {
+            openStream();
+            makeReporter();
+        }
+
+        Totals runTests() {
+
+            RunContext context( m_config.get(), m_reporter );
+
+            Totals totals;
+
+            context.testGroupStarting( "all tests", 1, 1 ); // deprecated?
+
+            TestSpec testSpec = m_config->testSpec();
+            if( !testSpec.hasFilters() )
+                testSpec = TestSpecParser( ITagAliasRegistry::get() ).parse( "~[.]" ).testSpec(); // All not hidden tests
+
+            std::vector<TestCase> testCases;
+            getRegistryHub().getTestCaseRegistry().getFilteredTests( testSpec, *m_config, testCases );
+
+            int testsRunForGroup = 0;
+            for( std::vector<TestCase>::const_iterator it = testCases.begin(), itEnd = testCases.end();
+                    it != itEnd;
+                    ++it ) {
+                testsRunForGroup++;
+                if( m_testsAlreadyRun.find( *it ) == m_testsAlreadyRun.end() ) {
+
+                    if( context.aborting() )
+                        break;
+
+                    totals += context.runTest( *it );
+                    m_testsAlreadyRun.insert( *it );
+                }
+            }
+            std::vector<TestCase> skippedTestCases;
+            getRegistryHub().getTestCaseRegistry().getFilteredTests( testSpec, *m_config, skippedTestCases, true );
+
+            for( std::vector<TestCase>::const_iterator it = skippedTestCases.begin(), itEnd = skippedTestCases.end();
+                    it != itEnd;
+                    ++it )
+                m_reporter->skipTest( *it );
+
+            context.testGroupEnded( "all tests", totals, 1, 1 );
+            return totals;
+        }
+
+    private:
+        void openStream() {
+            // Open output file, if specified
+            if( !m_config->getFilename().empty() ) {
+                m_ofs.open( m_config->getFilename().c_str() );
+                if( m_ofs.fail() ) {
+                    std::ostringstream oss;
+                    oss << "Unable to open file: '" << m_config->getFilename() << "'";
+                    throw std::domain_error( oss.str() );
+                }
+                m_config->setStreamBuf( m_ofs.rdbuf() );
+            }
+        }
+        void makeReporter() {
+            std::string reporterName = m_config->getReporterName().empty()
+                ? "console"
+                : m_config->getReporterName();
+
+            m_reporter = getRegistryHub().getReporterRegistry().create( reporterName, m_config.get() );
+            if( !m_reporter ) {
+                std::ostringstream oss;
+                oss << "No reporter registered with name: '" << reporterName << "'";
+                throw std::domain_error( oss.str() );
+            }
+        }
+
+    private:
+        Ptr<Config> m_config;
+        std::ofstream m_ofs;
+        Ptr<IStreamingReporter> m_reporter;
+        std::set<TestCase> m_testsAlreadyRun;
+    };
+
+    class Session : NonCopyable {
+        static bool alreadyInstantiated;
+
+    public:
+
+        struct OnUnusedOptions { enum DoWhat { Ignore, Fail }; };
+
+        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;
+        }
+        ~Session() {
+            Catch::cleanUp();
+        }
+
+        void showHelp( std::string const& processName ) {
+            Catch::cout() << "\nCatch v" << libraryVersion << "\n";
+
+            m_cli.usage( Catch::cout(), processName );
+            Catch::cout() << "For more detail usage please see the project docs\n" << std::endl;
+        }
+
+        int applyCommandLine( int argc, char* const argv[], OnUnusedOptions::DoWhat unusedOptionBehaviour = OnUnusedOptions::Fail ) {
+            try {
+                m_cli.setThrowOnUnrecognisedTokens( unusedOptionBehaviour == OnUnusedOptions::Fail );
+                m_unusedTokens = m_cli.parseInto( argc, argv, m_configData );
+                if( m_configData.showHelp )
+                    showHelp( m_configData.processName );
+                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;
+        }
+
+        void useConfigData( ConfigData const& _configData ) {
+            m_configData = _configData;
+            m_config.reset();
+        }
+
+        int run( int argc, char* const argv[] ) {
+
+            int returnCode = applyCommandLine( argc, argv );
+            if( returnCode == 0 )
+                returnCode = run();
+            return returnCode;
+        }
+
+        int run() {
+            if( m_configData.showHelp )
+                return 0;
+
+            try
+            {
+                config(); // Force config to be constructed
+
+                std::srand( m_configData.rngSeed );
+
+                Runner runner( m_config );
+
+                // Handle list request
+                if( Option<std::size_t> listed = list( config() ) )
+                    return static_cast<int>( *listed );
+
+                return static_cast<int>( runner.runTests().assertions.failed );
+            }
+            catch( std::exception& ex ) {
+                Catch::cerr() << ex.what() << std::endl;
+                return (std::numeric_limits<int>::max)();
+            }
+        }
+
+        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:
+        Clara::CommandLine<ConfigData> m_cli;
+        std::vector<Clara::Parser::Token> m_unusedTokens;
+        ConfigData m_configData;
+        Ptr<Config> m_config;
+    };
+
+    bool Session::alreadyInstantiated = false;
+
+} // end namespace Catch
+
+// #included from: catch_registry_hub.hpp
+#define TWOBLUECUBES_CATCH_REGISTRY_HUB_HPP_INCLUDED
+
+// #included from: catch_test_case_registry_impl.hpp
+#define TWOBLUECUBES_CATCH_TEST_CASE_REGISTRY_IMPL_HPP_INCLUDED
+
+#include <vector>
+#include <set>
+#include <sstream>
+#include <iostream>
+#include <algorithm>
+
+namespace Catch {
+
+    class TestRegistry : public ITestCaseRegistry {
+        struct LexSort {
+            bool operator() (TestCase i,TestCase j) const { return (i<j);}
+        };
+        struct RandomNumberGenerator {
+            int operator()( int n ) const { return std::rand() % n; }
+        };
+
+    public:
+        TestRegistry() : m_unnamedCount( 0 ) {}
+        virtual ~TestRegistry();
+
+        virtual void registerTest( TestCase const& testCase ) {
+            std::string name = testCase.getTestCaseInfo().name;
+            if( name == "" ) {
+                std::ostringstream oss;
+                oss << "Anonymous test case " << ++m_unnamedCount;
+                return registerTest( testCase.withName( oss.str() ) );
+            }
+
+            if( m_functions.find( testCase ) == m_functions.end() ) {
+                m_functions.insert( testCase );
+                m_functionsInOrder.push_back( testCase );
+                if( !testCase.isHidden() )
+                    m_nonHiddenFunctions.push_back( testCase );
+            }
+            else {
+                TestCase const& prev = *m_functions.find( testCase );
+                {
+                    Colour colourGuard( Colour::Red );
+                    Catch::cerr()   << "error: TEST_CASE( \"" << name << "\" ) already defined.\n"
+                                << "\tFirst seen at " << prev.getTestCaseInfo().lineInfo << "\n"
+                                << "\tRedefined at " << testCase.getTestCaseInfo().lineInfo << std::endl;
+                }
+                exit(1);
+            }
+        }
+
+        virtual std::vector<TestCase> const& getAllTests() const {
+            return m_functionsInOrder;
+        }
+
+        virtual std::vector<TestCase> const& getAllNonHiddenTests() const {
+            return m_nonHiddenFunctions;
+        }
+
+        virtual void getFilteredTests( TestSpec const& testSpec, IConfig const& config, std::vector<TestCase>& matchingTestCases, bool negated = false ) const {
+
+            for( std::vector<TestCase>::const_iterator  it = m_functionsInOrder.begin(),
+                                                        itEnd = m_functionsInOrder.end();
+                    it != itEnd;
+                    ++it ) {
+                bool includeTest = testSpec.matches( *it ) && ( config.allowThrows() || !it->throws() );
+                if( includeTest != negated )
+                    matchingTestCases.push_back( *it );
+            }
+            sortTests( config, matchingTestCases );
+        }
+
+    private:
+
+        static void sortTests( IConfig const& config, std::vector<TestCase>& matchingTestCases ) {
+
+            switch( config.runOrder() ) {
+                case RunTests::InLexicographicalOrder:
+                    std::sort( matchingTestCases.begin(), matchingTestCases.end(), LexSort() );
+                    break;
+                case RunTests::InRandomOrder:
+                {
+                    RandomNumberGenerator rng;
+                    std::random_shuffle( matchingTestCases.begin(), matchingTestCases.end(), rng );
+                }
+                    break;
+                case RunTests::InDeclarationOrder:
+                    // already in declaration order
+                    break;
+            }
+        }
+        std::set<TestCase> m_functions;
+        std::vector<TestCase> m_functionsInOrder;
+        std::vector<TestCase> m_nonHiddenFunctions;
+        size_t m_unnamedCount;
+    };
+
+    ///////////////////////////////////////////////////////////////////////////
+
+    class FreeFunctionTestCase : public SharedImpl<ITestCase> {
+    public:
+
+        FreeFunctionTestCase( TestFunction fun ) : m_fun( fun ) {}
+
+        virtual void invoke() const {
+            m_fun();
+        }
+
+    private:
+        virtual ~FreeFunctionTestCase();
+
+        TestFunction m_fun;
+    };
+
+    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;
+    }
+
+    ///////////////////////////////////////////////////////////////////////////
+
+    AutoReg::AutoReg(   TestFunction function,
+                        SourceLineInfo const& lineInfo,
+                        NameAndDesc const& nameAndDesc ) {
+        registerTestCase( new FreeFunctionTestCase( function ), "", nameAndDesc, lineInfo );
+    }
+
+    AutoReg::~AutoReg() {}
+
+    void AutoReg::registerTestCase( ITestCase* testCase,
+                                    char const* classOrQualifiedMethodName,
+                                    NameAndDesc const& nameAndDesc,
+                                    SourceLineInfo const& lineInfo ) {
+
+        getMutableRegistryHub().registerTest
+            ( makeTestCase( testCase,
+                            extractClassName( classOrQualifiedMethodName ),
+                            nameAndDesc.name,
+                            nameAndDesc.description,
+                            lineInfo ) );
+    }
+
+} // end namespace Catch
+
+// #included from: catch_reporter_registry.hpp
+#define TWOBLUECUBES_CATCH_REPORTER_REGISTRY_HPP_INCLUDED
+
+#include <map>
+
+namespace Catch {
+
+    class ReporterRegistry : public IReporterRegistry {
+
+    public:
+
+        virtual ~ReporterRegistry() {
+            deleteAllValues( m_factories );
+        }
+
+        virtual IStreamingReporter* create( std::string const& name, Ptr<IConfig> const& config ) const {
+            FactoryMap::const_iterator it =  m_factories.find( name );
+            if( it == m_factories.end() )
+                return NULL;
+            return it->second->create( ReporterConfig( config ) );
+        }
+
+        void registerReporter( std::string const& name, IReporterFactory* factory ) {
+            m_factories.insert( std::make_pair( name, factory ) );
+        }
+
+        FactoryMap const& getFactories() const {
+            return m_factories;
+        }
+
+    private:
+        FactoryMap m_factories;
+    };
+}
+
+// #included from: catch_exception_translator_registry.hpp
+#define TWOBLUECUBES_CATCH_EXCEPTION_TRANSLATOR_REGISTRY_HPP_INCLUDED
+
+#ifdef __OBJC__
+#import "Foundation/Foundation.h"
+#endif
+
+namespace Catch {
+
+    class ExceptionTranslatorRegistry : public IExceptionTranslatorRegistry {
+    public:
+        ~ExceptionTranslatorRegistry() {
+            deleteAll( m_translators );
+        }
+
+        virtual void registerTranslator( const IExceptionTranslator* translator ) {
+            m_translators.push_back( translator );
+        }
+
+        virtual std::string translateActiveException() const {
+            try {
+#ifdef __OBJC__
+                // In Objective-C try objective-c exceptions first
+                @try {
+                    throw;
+                }
+                @catch (NSException *exception) {
+                    return Catch::toString( [exception description] );
+                }
+#else
+                throw;
+#endif
+            }
+            catch( TestFailureException& ) {
+                throw;
+            }
+            catch( std::exception& ex ) {
+                return ex.what();
+            }
+            catch( std::string& msg ) {
+                return msg;
+            }
+            catch( const char* msg ) {
+                return msg;
+            }
+            catch(...) {
+                return tryTranslators( m_translators.begin() );
+            }
+        }
+
+        std::string tryTranslators( std::vector<const IExceptionTranslator*>::const_iterator it ) const {
+            if( it == m_translators.end() )
+                return "Unknown exception";
+
+            try {
+                return (*it)->translate();
+            }
+            catch(...) {
+                return tryTranslators( it+1 );
+            }
+        }
+
+    private:
+        std::vector<const IExceptionTranslator*> m_translators;
+    };
+}
+
+namespace Catch {
+
+    namespace {
+
+        class RegistryHub : public IRegistryHub, public IMutableRegistryHub {
+
+            RegistryHub( RegistryHub const& );
+            void operator=( RegistryHub const& );
+
+        public: // IRegistryHub
+            RegistryHub() {
+            }
+            virtual IReporterRegistry const& getReporterRegistry() const {
+                return m_reporterRegistry;
+            }
+            virtual ITestCaseRegistry const& getTestCaseRegistry() const {
+                return m_testCaseRegistry;
+            }
+            virtual IExceptionTranslatorRegistry& getExceptionTranslatorRegistry() {
+                return m_exceptionTranslatorRegistry;
+            }
+
+        public: // IMutableRegistryHub
+            virtual void registerReporter( std::string const& name, IReporterFactory* factory ) {
+                m_reporterRegistry.registerReporter( name, factory );
+            }
+            virtual void registerTest( TestCase const& testInfo ) {
+                m_testCaseRegistry.registerTest( testInfo );
+            }
+            virtual void registerTranslator( const IExceptionTranslator* translator ) {
+                m_exceptionTranslatorRegistry.registerTranslator( translator );
+            }
+
+        private:
+            TestRegistry m_testCaseRegistry;
+            ReporterRegistry m_reporterRegistry;
+            ExceptionTranslatorRegistry m_exceptionTranslatorRegistry;
+        };
+
+        // Single, global, instance
+        inline RegistryHub*& getTheRegistryHub() {
+            static RegistryHub* theRegistryHub = NULL;
+            if( !theRegistryHub )
+                theRegistryHub = new RegistryHub();
+            return theRegistryHub;
+        }
+    }
+
+    IRegistryHub& getRegistryHub() {
+        return *getTheRegistryHub();
+    }
+    IMutableRegistryHub& getMutableRegistryHub() {
+        return *getTheRegistryHub();
+    }
+    void cleanUp() {
+        delete getTheRegistryHub();
+        getTheRegistryHub() = NULL;
+        cleanUpContext();
+    }
+    std::string translateActiveException() {
+        return getRegistryHub().getExceptionTranslatorRegistry().translateActiveException();
+    }
+
+} // end namespace Catch
+
+// #included from: catch_notimplemented_exception.hpp
+#define TWOBLUECUBES_CATCH_NOTIMPLEMENTED_EXCEPTION_HPP_INCLUDED
+
+#include <ostream>
+
+namespace Catch {
+
+    NotImplementedException::NotImplementedException( SourceLineInfo const& lineInfo )
+    :   m_lineInfo( lineInfo ) {
+        std::ostringstream oss;
+        oss << lineInfo << ": function ";
+        oss << "not implemented";
+        m_what = oss.str();
+    }
+
+    const char* NotImplementedException::what() const CATCH_NOEXCEPT {
+        return m_what.c_str();
+    }
+
+} // end namespace Catch
+
+// #included from: catch_context_impl.hpp
+#define TWOBLUECUBES_CATCH_CONTEXT_IMPL_HPP_INCLUDED
+
+// #included from: catch_stream.hpp
+#define TWOBLUECUBES_CATCH_STREAM_HPP_INCLUDED
+
+// #included from: catch_streambuf.h
+#define TWOBLUECUBES_CATCH_STREAMBUF_H_INCLUDED
+
+#include <streambuf>
+
+namespace Catch {
+
+    class StreamBufBase : public std::streambuf {
+    public:
+        virtual ~StreamBufBase() CATCH_NOEXCEPT;
+    };
+}
+
+#include <stdexcept>
+#include <cstdio>
+#include <iostream>
+
+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) );
+        }
+
+        ~StreamBufImpl() CATCH_NOEXCEPT {
+            sync();
+        }
+
+    private:
+        int overflow( int c ) {
+            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() {
+            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 );
+        }
+    };
+
+    Stream::Stream()
+    : streamBuf( NULL ), isOwned( false )
+    {}
+
+    Stream::Stream( std::streambuf* _streamBuf, bool _isOwned )
+    : streamBuf( _streamBuf ), isOwned( _isOwned )
+    {}
+
+    void Stream::release() {
+        if( isOwned ) {
+            delete streamBuf;
+            streamBuf = NULL;
+            isOwned = false;
+        }
+    }
+
+#ifndef CATCH_CONFIG_NOSTDOUT // If you #define this you must implement this functions
+    std::ostream& cout() {
+        return std::cout;
+    }
+    std::ostream& cerr() {
+        return std::cerr;
+    }
+#endif
+}
+
+namespace Catch {
+
+    class Context : public IMutableContext {
+
+        Context() : m_config( NULL ), m_runner( NULL ), m_resultCapture( NULL ) {}
+        Context( Context const& );
+        void operator=( Context const& );
+
+    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();
+        }
+
+        virtual Ptr<IConfig const> getConfig() const {
+            return m_config;
+        }
+
+    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;
+        }
+
+        friend IMutableContext& getCurrentMutableContext();
+
+    private:
+        IGeneratorsForTest* findGeneratorsForCurrentTest() {
+            std::string testName = getResultCapture()->getCurrentTestName();
+
+            std::map<std::string, IGeneratorsForTest*>::const_iterator it =
+                m_generatorsByTestName.find( testName );
+            return it != m_generatorsByTestName.end()
+                ? it->second
+                : NULL;
+        }
+
+        IGeneratorsForTest& getGeneratorsForCurrentTest() {
+            IGeneratorsForTest* generators = findGeneratorsForCurrentTest();
+            if( !generators ) {
+                std::string testName = getResultCapture()->getCurrentTestName();
+                generators = createGeneratorsForTest();
+                m_generatorsByTestName.insert( std::make_pair( testName, generators ) );
+            }
+            return *generators;
+        }
+
+    private:
+        Ptr<IConfig const> m_config;
+        IRunner* m_runner;
+        IResultCapture* m_resultCapture;
+        std::map<std::string, IGeneratorsForTest*> m_generatorsByTestName;
+    };
+
+    namespace {
+        Context* currentContext = NULL;
+    }
+    IMutableContext& getCurrentMutableContext() {
+        if( !currentContext )
+            currentContext = new Context();
+        return *currentContext;
+    }
+    IContext& getCurrentContext() {
+        return getCurrentMutableContext();
+    }
+
+    Stream createStream( std::string const& streamName ) {
+        if( streamName == "stdout" ) return Stream( Catch::cout().rdbuf(), false );
+        if( streamName == "stderr" ) return Stream( Catch::cerr().rdbuf(), false );
+        if( streamName == "debug" ) return Stream( new StreamBufImpl<OutputDebugWriter>, true );
+
+        throw std::domain_error( "Unknown stream: " + streamName );
+    }
+
+    void cleanUpContext() {
+        delete currentContext;
+        currentContext = NULL;
+    }
+}
+
+// #included from: catch_console_colour_impl.hpp
+#define TWOBLUECUBES_CATCH_CONSOLE_COLOUR_IMPL_HPP_INCLUDED
+
+namespace Catch {
+    namespace {
+
+        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;
+            }
+        };
+
+    } // 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 ) /////////////////////////////////////////
+
+#ifndef NOMINMAX
+#define NOMINMAX
+#endif
+
+#ifdef __AFXDLL
+#include <AfxWin.h>
+#else
+#include <windows.h>
+#endif
+
+namespace Catch {
+namespace {
+
+    class Win32ColourImpl : public IColourImpl {
+    public:
+        Win32ColourImpl() : stdoutHandle( GetStdHandle(STD_OUTPUT_HANDLE) )
+        {
+            CONSOLE_SCREEN_BUFFER_INFO csbiInfo;
+            GetConsoleScreenBufferInfo( stdoutHandle, &csbiInfo );
+            originalAttributes = csbiInfo.wAttributes;
+        }
+
+        virtual void use( Colour::Code _colourCode ) {
+            switch( _colourCode ) {
+                case Colour::None:      return setTextAttribute( originalAttributes );
+                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::Bright: throw std::logic_error( "not a colour" );
+            }
+        }
+
+    private:
+        void setTextAttribute( WORD _textAttribute ) {
+            SetConsoleTextAttribute( stdoutHandle, _textAttribute );
+        }
+        HANDLE stdoutHandle;
+        WORD originalAttributes;
+    };
+
+    IColourImpl* platformColourInstance() {
+        static Win32ColourImpl s_instance;
+        return &s_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:
+        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" );
+
+                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::Bright: throw std::logic_error( "not a colour" );
+            }
+        }
+        static IColourImpl* instance() {
+            static PosixColourImpl s_instance;
+            return &s_instance;
+        }
+
+    private:
+        void setColour( const char* _escapeCode ) {
+            Catch::cout() << '\033' << _escapeCode;
+        }
+    };
+
+    IColourImpl* platformColourInstance() {
+        Ptr<IConfig const> config = getCurrentContext().getConfig();
+        return (config && config->forceColour()) || isatty(STDOUT_FILENO)
+            ? 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 ) : 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 ); }
+
+    void Colour::use( Code _colourCode ) {
+        static IColourImpl* impl = isDebuggerActive()
+            ? NoColourImpl::instance()
+            : platformColourInstance();
+        impl->use( _colourCode );
+    }
+
+} // end namespace Catch
+
+// #included from: catch_generators_impl.hpp
+#define TWOBLUECUBES_CATCH_GENERATORS_IMPL_HPP_INCLUDED
+
+#include <vector>
+#include <string>
+#include <map>
+
+namespace Catch {
+
+    struct GeneratorInfo : IGeneratorInfo {
+
+        GeneratorInfo( std::size_t size )
+        :   m_size( size ),
+            m_currentIndex( 0 )
+        {}
+
+        bool moveNext() {
+            if( ++m_currentIndex == m_size ) {
+                m_currentIndex = 0;
+                return false;
+            }
+            return true;
+        }
+
+        std::size_t getCurrentIndex() const {
+            return m_currentIndex;
+        }
+
+        std::size_t m_size;
+        std::size_t m_currentIndex;
+    };
+
+    ///////////////////////////////////////////////////////////////////////////
+
+    class GeneratorsForTest : public IGeneratorsForTest {
+
+    public:
+        ~GeneratorsForTest() {
+            deleteAll( m_generatorsInOrder );
+        }
+
+        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;
+        }
+
+        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;
+            }
+            return false;
+        }
+
+    private:
+        std::map<std::string, IGeneratorInfo*> m_generatorsByName;
+        std::vector<IGeneratorInfo*> m_generatorsInOrder;
+    };
+
+    IGeneratorsForTest* createGeneratorsForTest()
+    {
+        return new GeneratorsForTest();
+    }
+
+} // end namespace Catch
+
+// #included from: catch_assertionresult.hpp
+#define TWOBLUECUBES_CATCH_ASSERTIONRESULT_HPP_INCLUDED
+
+namespace Catch {
+
+    AssertionInfo::AssertionInfo(   std::string const& _macroName,
+                                    SourceLineInfo const& _lineInfo,
+                                    std::string const& _capturedExpression,
+                                    ResultDisposition::Flags _resultDisposition )
+    :   macroName( _macroName ),
+        lineInfo( _lineInfo ),
+        capturedExpression( _capturedExpression ),
+        resultDisposition( _resultDisposition )
+    {}
+
+    AssertionResult::AssertionResult() {}
+
+    AssertionResult::AssertionResult( AssertionInfo const& info, AssertionResultData const& data )
+    :   m_info( info ),
+        m_resultData( data )
+    {}
+
+    AssertionResult::~AssertionResult() {}
+
+    // 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 {
+        if( isFalseTest( m_info.resultDisposition ) )
+            return "!" + m_info.capturedExpression;
+        else
+            return m_info.capturedExpression;
+    }
+    std::string AssertionResult::getExpressionInMacro() const {
+        if( m_info.macroName.empty() )
+            return m_info.capturedExpression;
+        else
+            return m_info.macroName + "( " + m_info.capturedExpression + " )";
+    }
+
+    bool AssertionResult::hasExpandedExpression() const {
+        return hasExpression() && getExpandedExpression() != getExpression();
+    }
+
+    std::string AssertionResult::getExpandedExpression() const {
+        return m_resultData.reconstructedExpression;
+    }
+
+    std::string AssertionResult::getMessage() const {
+        return m_resultData.message;
+    }
+    SourceLineInfo AssertionResult::getSourceInfo() const {
+        return m_info.lineInfo;
+    }
+
+    std::string AssertionResult::getTestMacroName() const {
+        return m_info.macroName;
+    }
+
+} // end namespace Catch
+
+// #included from: catch_test_case_info.hpp
+#define TWOBLUECUBES_CATCH_TEST_CASE_INFO_HPP_INCLUDED
+
+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
+            return TestCaseInfo::None;
+    }
+    inline bool isReservedTag( std::string const& tag ) {
+        return parseSpecialTag( tag ) == TestCaseInfo::None && tag.size() > 0 && !isalnum( tag[0] );
+    }
+    inline void enforceNotReservedTag( std::string const& tag, SourceLineInfo const& _lineInfo ) {
+        if( isReservedTag( tag ) ) {
+            {
+                Colour colourGuard( Colour::Red );
+                Catch::cerr()
+                    << "Tag name [" << tag << "] not allowed.\n"
+                    << "Tag names starting with non alpha-numeric characters are reserved\n";
+            }
+            {
+                Colour colourGuard( Colour::FileName );
+                Catch::cerr() << _lineInfo << std::endl;
+            }
+            exit(1);
+        }
+    }
+
+    TestCase makeTestCase(  ITestCase* _testCase,
+                            std::string const& _className,
+                            std::string const& _name,
+                            std::string const& _descOrTags,
+                            SourceLineInfo const& _lineInfo )
+    {
+        bool isHidden( startsWith( _name, "./" ) ); // Legacy support
+
+        // Parse out tags
+        std::set<std::string> tags;
+        std::string desc, tag;
+        bool inTag = false;
+        for( std::size_t i = 0; i < _descOrTags.size(); ++i ) {
+            char c = _descOrTags[i];
+            if( !inTag ) {
+                if( c == '[' )
+                    inTag = true;
+                else
+                    desc += c;
+            }
+            else {
+                if( c == ']' ) {
+                    TestCaseInfo::SpecialProperties prop = parseSpecialTag( tag );
+                    if( prop == TestCaseInfo::IsHidden )
+                        isHidden = true;
+                    else if( prop == TestCaseInfo::None )
+                        enforceNotReservedTag( tag, _lineInfo );
+
+                    tags.insert( tag );
+                    tag.clear();
+                    inTag = false;
+                }
+                else
+                    tag += c;
+            }
+        }
+        if( isHidden ) {
+            tags.insert( "hide" );
+            tags.insert( "." );
+        }
+
+        TestCaseInfo info( _name, _className, desc, tags, _lineInfo );
+        return TestCase( _testCase, info );
+    }
+
+    TestCaseInfo::TestCaseInfo( std::string const& _name,
+                                std::string const& _className,
+                                std::string const& _description,
+                                std::set<std::string> const& _tags,
+                                SourceLineInfo const& _lineInfo )
+    :   name( _name ),
+        className( _className ),
+        description( _description ),
+        tags( _tags ),
+        lineInfo( _lineInfo ),
+        properties( None )
+    {
+        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 );
+            properties = static_cast<SpecialProperties>( properties | parseSpecialTag( lcaseTag ) );
+            lcaseTags.insert( lcaseTag );
+        }
+        tagsAsString = oss.str();
+    }
+
+    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;
+    }
+    bool TestCaseInfo::throws() const {
+        return ( properties & Throws ) != 0;
+    }
+    bool TestCaseInfo::okToFail() const {
+        return ( properties & (ShouldFail | MayFail ) ) != 0;
+    }
+    bool TestCaseInfo::expectedToFail() const {
+        return ( properties & (ShouldFail ) ) != 0;
+    }
+
+    TestCase::TestCase( ITestCase* testCase, TestCaseInfo const& info ) : TestCaseInfo( info ), test( testCase ) {}
+
+    TestCase::TestCase( TestCase const& other )
+    :   TestCaseInfo( other ),
+        test( other.test )
+    {}
+
+    TestCase TestCase::withName( std::string const& _newName ) const {
+        TestCase other( *this );
+        other.name = _newName;
+        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();
+    }
+
+    bool TestCase::operator == ( TestCase const& other ) const {
+        return  test.get() == other.test.get() &&
+                name == other.name &&
+                className == other.className;
+    }
+
+    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
+    {
+        return *this;
+    }
+
+} // end namespace Catch
+
+// #included from: catch_version.hpp
+#define TWOBLUECUBES_CATCH_VERSION_HPP_INCLUDED
+
+namespace Catch {
+
+    Version::Version
+        (   unsigned int _majorVersion,
+            unsigned int _minorVersion,
+            unsigned int _patchNumber,
+            std::string 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;
+
+        if( !version.branchName.empty() ) {
+            os  << "-" << version.branchName
+                << "." << version.buildNumber;
+        }
+        return os;
+    }
+
+    Version libraryVersion( 1, 2, 1, "", 0 );
+
+}
+
+// #included from: catch_message.hpp
+#define TWOBLUECUBES_CATCH_MESSAGE_HPP_INCLUDED
+
+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 )
+    {}
+
+    ScopedMessage::~ScopedMessage() {
+        getResultCapture().popScopedMessage( m_info );
+    }
+
+} // 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 );
+                }
+            }
+        }
+        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
+#include <windows.h>
+#else
+#include <sys/time.h>
+#endif
+
+namespace Catch {
+
+    namespace {
+#ifdef CATCH_PLATFORM_WINDOWS
+        uint64_t getCurrentTicks() {
+            static uint64_t hz=0, hzo=0;
+            if (!hz) {
+                QueryPerformanceFrequency( reinterpret_cast<LARGE_INTEGER*>( &hz ) );
+                QueryPerformanceCounter( reinterpret_cast<LARGE_INTEGER*>( &hzo ) );
+            }
+            uint64_t t;
+            QueryPerformanceCounter( reinterpret_cast<LARGE_INTEGER*>( &t ) );
+            return ((t-hzo)*1000000)/hz;
+        }
+#else
+        uint64_t getCurrentTicks() {
+            timeval t;
+            gettimeofday(&t,NULL);
+            return static_cast<uint64_t>( t.tv_sec ) * 1000000ull + static_cast<uint64_t>( 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
+
+namespace Catch {
+
+    bool startsWith( std::string const& s, std::string const& prefix ) {
+        return s.size() >= prefix.size() && s.substr( 0, prefix.size() ) == prefix;
+    }
+    bool endsWith( std::string const& s, std::string const& suffix ) {
+        return s.size() >= suffix.size() && s.substr( s.size()-suffix.size(), suffix.size() ) == 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(), ::tolower );
+    }
+    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 ) : "";
+    }
+
+    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;
+    }
+
+    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;
+    }
+
+    SourceLineInfo::SourceLineInfo() : line( 0 ){}
+    SourceLineInfo::SourceLineInfo( char const* _file, std::size_t _line )
+    :   file( _file ),
+        line( _line )
+    {}
+    SourceLineInfo::SourceLineInfo( SourceLineInfo const& other )
+    :   file( other.file ),
+        line( other.line )
+    {}
+    bool SourceLineInfo::empty() const {
+        return file.empty();
+    }
+    bool SourceLineInfo::operator == ( SourceLineInfo const& other ) const {
+        return line == other.line && file == other.file;
+    }
+    bool SourceLineInfo::operator < ( SourceLineInfo const& other ) const {
+        return line < other.line || ( line == other.line  && file < other.file );
+    }
+
+    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 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() );
+    }
+}
+
+// #included from: catch_section.hpp
+#define TWOBLUECUBES_CATCH_SECTION_HPP_INCLUDED
+
+namespace Catch {
+
+    SectionInfo::SectionInfo
+        (   SourceLineInfo const& _lineInfo,
+            std::string const& _name,
+            std::string const& _description )
+    :   name( _name ),
+        description( _description ),
+        lineInfo( _lineInfo )
+    {}
+
+    Section::Section( SectionInfo const& info )
+    :   m_info( info ),
+        m_sectionIncluded( getResultCapture().sectionStarted( m_info, m_assertions ) )
+    {
+        m_timer.start();
+    }
+
+    Section::~Section() {
+        if( m_sectionIncluded )
+            getResultCapture().sectionEnded( m_info, m_assertions, m_timer.getElapsedSeconds() );
+    }
+
+    // This indicates whether the section should be executed or not
+    Section::operator bool() const {
+        return m_sectionIncluded;
+    }
+
+} // end namespace Catch
+
+// #included from: catch_debugger.hpp
+#define TWOBLUECUBES_CATCH_DEBUGGER_HPP_INCLUDED
+
+#include <iostream>
+
+#ifdef CATCH_PLATFORM_MAC
+
+    #include <assert.h>
+    #include <stdbool.h>
+    #include <sys/types.h>
+    #include <unistd.h>
+    #include <sys/sysctl.h>
+
+    namespace Catch{
+
+        // The following function is taken directly from the following technical note:
+        // http://developer.apple.com/library/mac/#qa/qa2004/qa1361.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;
+            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, NULL, 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 );
+        }
+    } // 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 {
+       inline bool isDebuggerActive() { return false; }
+    }
+#endif // Platform
+
+#ifdef CATCH_PLATFORM_WINDOWS
+    extern "C" __declspec(dllimport) void __stdcall OutputDebugStringA( const char* );
+    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
+
+// #included from: catch_tostring.hpp
+#define TWOBLUECUBES_CATCH_TOSTRING_HPP_INCLUDED
+
+namespace Catch {
+
+namespace Detail {
+
+    std::string unprintableString = "{?}";
+
+    namespace {
+        struct Endianness {
+            enum Arch { Big, Little };
+
+            static Arch which() {
+                union _{
+                    int asInt;
+                    char asChar[sizeof (int)];
+                } u;
+
+                u.asInt = 1;
+                return ( u.asChar[sizeof(int)-1] == 1 ) ? Big : Little;
+            }
+        };
+    }
+
+    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;
+        }
+
+        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();
+    }
+}
+
+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;
+            }
+        }
+    }
+    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 toString( const char* const value ) {
+    return value ? Catch::toString( std::string( value ) ) : std::string( "{null string}" );
+}
+
+std::string toString( char* const value ) {
+    return Catch::toString( static_cast<const char*>( value ) );
+}
+
+std::string toString( const wchar_t* const value )
+{
+	return value ? Catch::toString( std::wstring(value) ) : std::string( "{null string}" );
+}
+
+std::string toString( wchar_t* const value )
+{
+	return Catch::toString( static_cast<const wchar_t*>( value ) );
+}
+
+std::string toString( int value ) {
+    std::ostringstream oss;
+    oss << value;
+    if( value >= 255 )
+        oss << " (0x" << std::hex << value << ")";
+    return oss.str();
+}
+
+std::string toString( unsigned long value ) {
+    std::ostringstream oss;
+    oss << value;
+    if( value >= 255 )
+        oss << " (0x" << std::hex << value << ")";
+    return oss.str();
+}
+
+std::string toString( unsigned int value ) {
+    return Catch::toString( static_cast<unsigned long>( value ) );
+}
+
+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 );
+    }
+    return d;
+}
+
+std::string toString( const double value ) {
+    return fpToString( value, 10 );
+}
+std::string toString( const float value ) {
+    return fpToString( value, 5 ) + "f";
+}
+
+std::string toString( bool value ) {
+    return value ? "true" : "false";
+}
+
+std::string toString( char value ) {
+    return value < ' '
+        ? toString( static_cast<unsigned int>( value ) )
+        : Detail::makeString( value );
+}
+
+std::string toString( signed char value ) {
+    return toString( static_cast<char>( value ) );
+}
+
+std::string toString( unsigned char value ) {
+    return toString( static_cast<char>( value ) );
+}
+
+#ifdef CATCH_CONFIG_CPP11_NULLPTR
+std::string toString( std::nullptr_t ) {
+    return "nullptr";
+}
+#endif
+
+#ifdef __OBJC__
+    std::string toString( NSString const * const& nsstring ) {
+        if( !nsstring )
+            return "nil";
+        return "@" + toString([nsstring UTF8String]);
+    }
+    std::string toString( NSString * CATCH_ARC_STRONG const& nsstring ) {
+        if( !nsstring )
+            return "nil";
+        return "@" + toString([nsstring UTF8String]);
+    }
+    std::string toString( NSObject* const& nsObject ) {
+        return toString( [nsObject description] );
+    }
+#endif
+
+} // end namespace Catch
+
+// #included from: catch_result_builder.hpp
+#define TWOBLUECUBES_CATCH_RESULT_BUILDER_HPP_INCLUDED
+
+namespace Catch {
+
+    ResultBuilder::ResultBuilder(   char const* macroName,
+                                    SourceLineInfo const& lineInfo,
+                                    char const* capturedExpression,
+                                    ResultDisposition::Flags resultDisposition )
+    :   m_assertionInfo( macroName, lineInfo, capturedExpression, resultDisposition ),
+        m_shouldDebugBreak( false ),
+        m_shouldThrow( false )
+    {}
+
+    ResultBuilder& ResultBuilder::setResultType( ResultWas::OfType result ) {
+        m_data.resultType = result;
+        return *this;
+    }
+    ResultBuilder& ResultBuilder::setResultType( bool result ) {
+        m_data.resultType = result ? ResultWas::Ok : ResultWas::ExpressionFailed;
+        return *this;
+    }
+    ResultBuilder& ResultBuilder::setLhs( std::string const& lhs ) {
+        m_exprComponents.lhs = lhs;
+        return *this;
+    }
+    ResultBuilder& ResultBuilder::setRhs( std::string const& rhs ) {
+        m_exprComponents.rhs = rhs;
+        return *this;
+    }
+    ResultBuilder& ResultBuilder::setOp( std::string const& op ) {
+        m_exprComponents.op = op;
+        return *this;
+    }
+
+    void ResultBuilder::endExpression() {
+        m_exprComponents.testFalse = isFalseTest( m_assertionInfo.resultDisposition );
+        captureExpression();
+    }
+
+    void ResultBuilder::useActiveException( ResultDisposition::Flags resultDisposition ) {
+        m_assertionInfo.resultDisposition = resultDisposition;
+        m_stream.oss << Catch::translateActiveException();
+        captureResult( ResultWas::ThrewException );
+    }
+
+    void ResultBuilder::captureResult( ResultWas::OfType resultType ) {
+        setResultType( resultType );
+        captureExpression();
+    }
+
+    void ResultBuilder::captureExpression() {
+        AssertionResult result = build();
+        getResultCapture().assertionEnded( result );
+
+        if( !result.isOk() ) {
+            if( getCurrentContext().getConfig()->shouldDebugBreak() )
+                m_shouldDebugBreak = true;
+            if( getCurrentContext().getRunner()->aborting() || (m_assertionInfo.resultDisposition & ResultDisposition::Normal) )
+                m_shouldThrow = true;
+        }
+    }
+    void ResultBuilder::react() {
+        if( m_shouldThrow )
+            throw Catch::TestFailureException();
+    }
+
+    bool ResultBuilder::shouldDebugBreak() const { return m_shouldDebugBreak; }
+    bool ResultBuilder::allowThrows() const { return getCurrentContext().getConfig()->allowThrows(); }
+
+    AssertionResult ResultBuilder::build() const
+    {
+        assert( m_data.resultType != ResultWas::Unknown );
+
+        AssertionResultData data = m_data;
+
+        // Flip bool results if testFalse is set
+        if( m_exprComponents.testFalse ) {
+            if( data.resultType == ResultWas::Ok )
+                data.resultType = ResultWas::ExpressionFailed;
+            else if( data.resultType == ResultWas::ExpressionFailed )
+                data.resultType = ResultWas::Ok;
+        }
+
+        data.message = m_stream.oss.str();
+        data.reconstructedExpression = reconstructExpression();
+        if( m_exprComponents.testFalse ) {
+            if( m_exprComponents.op == "" )
+                data.reconstructedExpression = "!" + data.reconstructedExpression;
+            else
+                data.reconstructedExpression = "!(" + data.reconstructedExpression + ")";
+        }
+        return AssertionResult( m_assertionInfo, data );
+    }
+    std::string ResultBuilder::reconstructExpression() const {
+        if( m_exprComponents.op == "" )
+            return m_exprComponents.lhs.empty() ? m_assertionInfo.capturedExpression : m_exprComponents.op + m_exprComponents.lhs;
+        else if( m_exprComponents.op == "matches" )
+            return m_exprComponents.lhs + " " + m_exprComponents.rhs;
+        else if( m_exprComponents.op != "!" ) {
+            if( m_exprComponents.lhs.size() + m_exprComponents.rhs.size() < 40 &&
+                m_exprComponents.lhs.find("\n") == std::string::npos &&
+                m_exprComponents.rhs.find("\n") == std::string::npos )
+                return m_exprComponents.lhs + " " + m_exprComponents.op + " " + m_exprComponents.rhs;
+            else
+                return m_exprComponents.lhs + "\n" + m_exprComponents.op + "\n" + m_exprComponents.rhs;
+        }
+        else
+            return "{can't expand - use " + m_assertionInfo.macroName + "_FALSE( " + m_assertionInfo.capturedExpression.substr(1) + " ) instead of " + m_assertionInfo.macroName + "( " + m_assertionInfo.capturedExpression + " ) for better diagnostics}";
+    }
+
+} // end namespace Catch
+
+// #included from: catch_tag_alias_registry.hpp
+#define TWOBLUECUBES_CATCH_TAG_ALIAS_REGISTRY_HPP_INCLUDED
+
+// #included from: catch_tag_alias_registry.h
+#define TWOBLUECUBES_CATCH_TAG_ALIAS_REGISTRY_H_INCLUDED
+
+#include <map>
+
+namespace Catch {
+
+    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( char const* alias, char const* tag, SourceLineInfo const& lineInfo );
+        static TagAliasRegistry& get();
+
+    private:
+        std::map<std::string, TagAlias> m_registry;
+    };
+
+} // end namespace Catch
+
+#include <map>
+#include <iostream>
+
+namespace Catch {
+
+    TagAliasRegistry::~TagAliasRegistry() {}
+
+    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>();
+    }
+
+    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;
+    }
+
+    void TagAliasRegistry::add( char const* alias, char const* tag, SourceLineInfo const& lineInfo ) {
+
+        if( !startsWith( alias, "[@" ) || !endsWith( alias, "]" ) ) {
+            std::ostringstream oss;
+            oss << "error: tag alias, \"" << alias << "\" is not of the form [@alias name].\n" << lineInfo;
+            throw std::domain_error( oss.str().c_str() );
+        }
+        if( !m_registry.insert( std::make_pair( alias, TagAlias( tag, lineInfo ) ) ).second ) {
+            std::ostringstream oss;
+            oss << "error: tag alias, \"" << alias << "\" already registered.\n"
+                << "\tFirst seen at " << find(alias)->lineInfo << "\n"
+                << "\tRedefined at " << lineInfo;
+            throw std::domain_error( oss.str().c_str() );
+        }
+    }
+
+    TagAliasRegistry& TagAliasRegistry::get() {
+        static TagAliasRegistry instance;
+        return instance;
+
+    }
+
+    ITagAliasRegistry::~ITagAliasRegistry() {}
+    ITagAliasRegistry const& ITagAliasRegistry::get() { return TagAliasRegistry::get(); }
+
+    RegistrarForTagAliases::RegistrarForTagAliases( char const* alias, char const* tag, SourceLineInfo const& lineInfo ) {
+        try {
+            TagAliasRegistry::get().add( alias, tag, lineInfo );
+        }
+        catch( std::exception& ex ) {
+            Colour colourGuard( Colour::Red );
+            Catch::cerr() << ex.what() << std::endl;
+            exit(1);
+        }
+    }
+
+} // end namespace Catch
+
+// #included from: ../reporters/catch_reporter_xml.hpp
+#define TWOBLUECUBES_CATCH_REPORTER_XML_HPP_INCLUDED
+
+// #included from: catch_reporter_bases.hpp
+#define TWOBLUECUBES_CATCH_REPORTER_BASES_HPP_INCLUDED
+
+#include <cstring>
+
+namespace Catch {
+
+    struct StreamingReporterBase : SharedImpl<IStreamingReporter> {
+
+        StreamingReporterBase( ReporterConfig const& _config )
+        :   m_config( _config.fullConfig() ),
+            stream( _config.stream() )
+        {}
+
+        virtual ~StreamingReporterBase();
+
+        virtual void noMatchingTestCases( std::string const& ) {}
+
+        virtual void testRunStarting( TestRunInfo const& _testRunInfo ) {
+            currentTestRunInfo = _testRunInfo;
+        }
+        virtual void testGroupStarting( GroupInfo const& _groupInfo ) {
+            currentGroupInfo = _groupInfo;
+        }
+
+        virtual void testCaseStarting( TestCaseInfo const& _testInfo ) {
+            currentTestCaseInfo = _testInfo;
+        }
+        virtual void sectionStarting( SectionInfo const& _sectionInfo ) {
+            m_sectionStack.push_back( _sectionInfo );
+        }
+
+        virtual void sectionEnded( SectionStats const& /* _sectionStats */ ) {
+            m_sectionStack.pop_back();
+        }
+        virtual void testCaseEnded( TestCaseStats const& /* _testCaseStats */ ) {
+            currentTestCaseInfo.reset();
+        }
+        virtual void testGroupEnded( TestGroupStats const& /* _testGroupStats */ ) {
+            currentGroupInfo.reset();
+        }
+        virtual void testRunEnded( TestRunStats const& /* _testRunStats */ ) {
+            currentTestCaseInfo.reset();
+            currentGroupInfo.reset();
+            currentTestRunInfo.reset();
+        }
+
+        virtual void skipTest( TestCaseInfo const& ) {
+            // Don't do anything with this by default.
+            // It can optionally be overridden in the derived class.
+        }
+
+        Ptr<IConfig> m_config;
+        std::ostream& stream;
+
+        LazyStat<TestRunInfo> currentTestRunInfo;
+        LazyStat<GroupInfo> currentGroupInfo;
+        LazyStat<TestCaseInfo> currentTestCaseInfo;
+
+        std::vector<SectionInfo> m_sectionStack;
+    };
+
+    struct CumulativeReporterBase : SharedImpl<IStreamingReporter> {
+        template<typename T, typename ChildNodeT>
+        struct Node : SharedImpl<> {
+            explicit Node( T const& _value ) : value( _value ) {}
+            virtual ~Node() {}
+
+            typedef std::vector<Ptr<ChildNodeT> > ChildNodes;
+            T value;
+            ChildNodes children;
+        };
+        struct SectionNode : SharedImpl<> {
+            explicit SectionNode( SectionStats const& _stats ) : stats( _stats ) {}
+            virtual ~SectionNode();
+
+            bool operator == ( SectionNode const& other ) const {
+                return stats.sectionInfo.lineInfo == other.stats.sectionInfo.lineInfo;
+            }
+            bool operator == ( Ptr<SectionNode> const& other ) const {
+                return operator==( *other );
+            }
+
+            SectionStats stats;
+            typedef std::vector<Ptr<SectionNode> > ChildSections;
+            typedef std::vector<AssertionStats> Assertions;
+            ChildSections childSections;
+            Assertions assertions;
+            std::string stdOut;
+            std::string stdErr;
+        };
+
+        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.lineInfo == m_other.lineInfo;
+            }
+        private:
+			void operator=( BySectionInfo const& );
+            SectionInfo const& m_other;
+        };
+
+        typedef Node<TestCaseStats, SectionNode> TestCaseNode;
+        typedef Node<TestGroupStats, TestCaseNode> TestGroupNode;
+        typedef Node<TestRunStats, TestGroupNode> TestRunNode;
+
+        CumulativeReporterBase( ReporterConfig const& _config )
+        :   m_config( _config.fullConfig() ),
+            stream( _config.stream() )
+        {}
+        ~CumulativeReporterBase();
+
+        virtual void testRunStarting( TestRunInfo const& ) {}
+        virtual void testGroupStarting( GroupInfo const& ) {}
+
+        virtual void testCaseStarting( TestCaseInfo const& ) {}
+
+        virtual void sectionStarting( SectionInfo const& sectionInfo ) {
+            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;
+            }
+            m_sectionStack.push_back( node );
+            m_deepestSection = node;
+        }
+
+        virtual void assertionStarting( AssertionInfo const& ) {}
+
+        virtual bool assertionEnded( AssertionStats const& assertionStats ) {
+            assert( !m_sectionStack.empty() );
+            SectionNode& sectionNode = *m_sectionStack.back();
+            sectionNode.assertions.push_back( assertionStats );
+            return true;
+        }
+        virtual void sectionEnded( SectionStats const& sectionStats ) {
+            assert( !m_sectionStack.empty() );
+            SectionNode& node = *m_sectionStack.back();
+            node.stats = sectionStats;
+            m_sectionStack.pop_back();
+        }
+        virtual void testCaseEnded( TestCaseStats const& testCaseStats ) {
+            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();
+
+            assert( m_deepestSection );
+            m_deepestSection->stdOut = testCaseStats.stdOut;
+            m_deepestSection->stdErr = testCaseStats.stdErr;
+        }
+        virtual void testGroupEnded( TestGroupStats const& testGroupStats ) {
+            Ptr<TestGroupNode> node = new TestGroupNode( testGroupStats );
+            node->children.swap( m_testCases );
+            m_testGroups.push_back( node );
+        }
+        virtual void testRunEnded( TestRunStats const& testRunStats ) {
+            Ptr<TestRunNode> node = new TestRunNode( testRunStats );
+            node->children.swap( m_testGroups );
+            m_testRuns.push_back( node );
+            testRunEndedCumulative();
+        }
+        virtual void testRunEndedCumulative() = 0;
+
+        virtual void skipTest( TestCaseInfo const& ) {}
+
+        Ptr<IConfig> 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;
+
+        std::vector<Ptr<TestRunNode> > m_testRuns;
+
+        Ptr<SectionNode> m_rootSection;
+        Ptr<SectionNode> m_deepestSection;
+        std::vector<Ptr<SectionNode> > m_sectionStack;
+
+    };
+
+    template<char C>
+    char const* getLineOfChars() {
+        static char line[CATCH_CONFIG_CONSOLE_WIDTH] = {0};
+        if( !*line ) {
+            memset( line, C, CATCH_CONFIG_CONSOLE_WIDTH-1 );
+            line[CATCH_CONFIG_CONSOLE_WIDTH-1] = 0;
+        }
+        return line;
+    }
+
+} // end namespace Catch
+
+// #included from: ../internal/catch_reporter_registrars.hpp
+#define TWOBLUECUBES_CATCH_REPORTER_REGISTRARS_HPP_INCLUDED
+
+namespace Catch {
+
+    template<typename T>
+    class LegacyReporterRegistrar {
+
+        class ReporterFactory : public IReporterFactory {
+            virtual IStreamingReporter* create( ReporterConfig const& config ) const {
+                return new LegacyReporterAdapter( new T( config ) );
+            }
+
+            virtual std::string getDescription() const {
+                return T::getDescription();
+            }
+        };
+
+    public:
+
+        LegacyReporterRegistrar( std::string const& name ) {
+            getMutableRegistryHub().registerReporter( name, new ReporterFactory() );
+        }
+    };
+
+    template<typename T>
+    class ReporterRegistrar {
+
+        class ReporterFactory : public IReporterFactory {
+
+            // *** 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 );
+            }
+
+            virtual std::string getDescription() const {
+                return T::getDescription();
+            }
+        };
+
+    public:
+
+        ReporterRegistrar( std::string const& name ) {
+            getMutableRegistryHub().registerReporter( name, new ReporterFactory() );
+        }
+    };
+}
+
+#define INTERNAL_CATCH_REGISTER_LEGACY_REPORTER( name, reporterType ) \
+    namespace{ Catch::LegacyReporterRegistrar<reporterType> catch_internal_RegistrarFor##reporterType( name ); }
+#define INTERNAL_CATCH_REGISTER_REPORTER( name, reporterType ) \
+    namespace{ Catch::ReporterRegistrar<reporterType> catch_internal_RegistrarFor##reporterType( name ); }
+
+// #included from: ../internal/catch_xmlwriter.hpp
+#define TWOBLUECUBES_CATCH_XMLWRITER_HPP_INCLUDED
+
+#include <sstream>
+#include <string>
+#include <vector>
+
+namespace Catch {
+
+    class XmlWriter {
+    public:
+
+        class ScopedElement {
+        public:
+            ScopedElement( XmlWriter* writer )
+            :   m_writer( writer )
+            {}
+
+            ScopedElement( ScopedElement const& other )
+            :   m_writer( other.m_writer ){
+                other.m_writer = NULL;
+            }
+
+            ~ScopedElement() {
+                if( m_writer )
+                    m_writer->endElement();
+            }
+
+            ScopedElement& writeText( std::string const& text, bool indent = true ) {
+                m_writer->writeText( text, indent );
+                return *this;
+            }
+
+            template<typename T>
+            ScopedElement& writeAttribute( std::string const& name, T const& attribute ) {
+                m_writer->writeAttribute( name, attribute );
+                return *this;
+            }
+
+        private:
+            mutable XmlWriter* m_writer;
+        };
+
+        XmlWriter()
+        :   m_tagIsOpen( false ),
+            m_needsNewline( false ),
+            m_os( &Catch::cout() )
+        {}
+
+        XmlWriter( std::ostream& os )
+        :   m_tagIsOpen( false ),
+            m_needsNewline( false ),
+            m_os( &os )
+        {}
+
+        ~XmlWriter() {
+            while( !m_tags.empty() )
+                endElement();
+        }
+
+        XmlWriter& startElement( std::string const& name ) {
+            ensureTagClosed();
+            newlineIfNecessary();
+            stream() << m_indent << "<" << name;
+            m_tags.push_back( name );
+            m_indent += "  ";
+            m_tagIsOpen = true;
+            return *this;
+        }
+
+        ScopedElement scopedElement( std::string const& name ) {
+            ScopedElement scoped( this );
+            startElement( name );
+            return scoped;
+        }
+
+        XmlWriter& endElement() {
+            newlineIfNecessary();
+            m_indent = m_indent.substr( 0, m_indent.size()-2 );
+            if( m_tagIsOpen ) {
+                stream() << "/>\n";
+                m_tagIsOpen = false;
+            }
+            else {
+                stream() << m_indent << "</" << m_tags.back() << ">\n";
+            }
+            m_tags.pop_back();
+            return *this;
+        }
+
+        XmlWriter& writeAttribute( std::string const& name, std::string const& attribute ) {
+            if( !name.empty() && !attribute.empty() ) {
+                stream() << " " << name << "=\"";
+                writeEncodedText( attribute );
+                stream() << "\"";
+            }
+            return *this;
+        }
+
+        XmlWriter& writeAttribute( std::string const& name, bool attribute ) {
+            stream() << " " << name << "=\"" << ( attribute ? "true" : "false" ) << "\"";
+            return *this;
+        }
+
+        template<typename T>
+        XmlWriter& writeAttribute( std::string const& name, T const& attribute ) {
+            if( !name.empty() )
+                stream() << " " << name << "=\"" << attribute << "\"";
+            return *this;
+        }
+
+        XmlWriter& writeText( std::string const& text, bool indent = true ) {
+            if( !text.empty() ){
+                bool tagWasOpen = m_tagIsOpen;
+                ensureTagClosed();
+                if( tagWasOpen && indent )
+                    stream() << m_indent;
+                writeEncodedText( text );
+                m_needsNewline = true;
+            }
+            return *this;
+        }
+
+        XmlWriter& writeComment( std::string const& text ) {
+            ensureTagClosed();
+            stream() << m_indent << "<!--" << text << "-->";
+            m_needsNewline = true;
+            return *this;
+        }
+
+        XmlWriter& writeBlankLine() {
+            ensureTagClosed();
+            stream() << "\n";
+            return *this;
+        }
+
+        void setStream( std::ostream& os ) {
+            m_os = &os;
+        }
+
+    private:
+        XmlWriter( XmlWriter const& );
+        void operator=( XmlWriter const& );
+
+        std::ostream& stream() {
+            return *m_os;
+        }
+
+        void ensureTagClosed() {
+            if( m_tagIsOpen ) {
+                stream() << ">\n";
+                m_tagIsOpen = false;
+            }
+        }
+
+        void newlineIfNecessary() {
+            if( m_needsNewline ) {
+                stream() << "\n";
+                m_needsNewline = false;
+            }
+        }
+
+        void writeEncodedText( std::string const& text ) {
+            static const char* charsToEncode = "<&\"";
+            std::string mtext = text;
+            std::string::size_type pos = mtext.find_first_of( charsToEncode );
+            while( pos != std::string::npos ) {
+                stream() << mtext.substr( 0, pos );
+
+                switch( mtext[pos] ) {
+                    case '<':
+                        stream() << "<";
+                        break;
+                    case '&':
+                        stream() << "&";
+                        break;
+                    case '\"':
+                        stream() << """;
+                        break;
+                }
+                mtext = mtext.substr( pos+1 );
+                pos = mtext.find_first_of( charsToEncode );
+            }
+            stream() << mtext;
+        }
+
+        bool m_tagIsOpen;
+        bool m_needsNewline;
+        std::vector<std::string> m_tags;
+        std::string m_indent;
+        std::ostream* m_os;
+    };
+
+}
+namespace Catch {
+    class XmlReporter : public StreamingReporterBase {
+    public:
+        XmlReporter( ReporterConfig const& _config )
+        :   StreamingReporterBase( _config ),
+            m_sectionDepth( 0 )
+        {}
+
+        virtual ~XmlReporter();
+
+        static std::string getDescription() {
+            return "Reports test results as an XML document";
+        }
+
+    public: // StreamingReporterBase
+        virtual ReporterPreferences getPreferences() const {
+            ReporterPreferences prefs;
+            prefs.shouldRedirectStdOut = true;
+            return prefs;
+        }
+
+        virtual void noMatchingTestCases( std::string const& s ) {
+            StreamingReporterBase::noMatchingTestCases( s );
+        }
+
+        virtual void testRunStarting( TestRunInfo const& testInfo ) {
+            StreamingReporterBase::testRunStarting( testInfo );
+            m_xml.setStream( stream );
+            m_xml.startElement( "Catch" );
+            if( !m_config->name().empty() )
+                m_xml.writeAttribute( "name", m_config->name() );
+        }
+
+        virtual void testGroupStarting( GroupInfo const& groupInfo ) {
+            StreamingReporterBase::testGroupStarting( groupInfo );
+            m_xml.startElement( "Group" )
+                .writeAttribute( "name", groupInfo.name );
+        }
+
+        virtual void testCaseStarting( TestCaseInfo const& testInfo ) {
+            StreamingReporterBase::testCaseStarting(testInfo);
+            m_xml.startElement( "TestCase" ).writeAttribute( "name", trim( testInfo.name ) );
+
+            if ( m_config->showDurations() == ShowDurations::Always )
+                m_testCaseTimer.start();
+        }
+
+        virtual void sectionStarting( SectionInfo const& sectionInfo ) {
+            StreamingReporterBase::sectionStarting( sectionInfo );
+            if( m_sectionDepth++ > 0 ) {
+                m_xml.startElement( "Section" )
+                    .writeAttribute( "name", trim( sectionInfo.name ) )
+                    .writeAttribute( "description", sectionInfo.description );
+            }
+        }
+
+        virtual void assertionStarting( AssertionInfo const& ) { }
+
+        virtual bool assertionEnded( AssertionStats const& assertionStats ) {
+            const AssertionResult& assertionResult = assertionStats.assertionResult;
+
+            // Print any info messages in <Info> tags.
+            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 ) {
+                        m_xml.scopedElement( "Info" )
+                            .writeText( it->message );
+                    } else if ( it->type == ResultWas::Warning ) {
+                        m_xml.scopedElement( "Warning" )
+                            .writeText( it->message );
+                    }
+                }
+            }
+
+            // Drop out if result was successful but we're not printing them.
+            if( !m_config->includeSuccessfulResults() && isOk(assertionResult.getResultType()) )
+                return true;
+
+            // Print the expression if there is one.
+            if( assertionResult.hasExpression() ) {
+                m_xml.startElement( "Expression" )
+                    .writeAttribute( "success", assertionResult.succeeded() )
+					.writeAttribute( "type", assertionResult.getTestMacroName() )
+                    .writeAttribute( "filename", assertionResult.getSourceInfo().file )
+                    .writeAttribute( "line", assertionResult.getSourceInfo().line );
+
+                m_xml.scopedElement( "Original" )
+                    .writeText( assertionResult.getExpression() );
+                m_xml.scopedElement( "Expanded" )
+                    .writeText( assertionResult.getExpandedExpression() );
+            }
+
+            // And... Print a result applicable to each result type.
+            switch( assertionResult.getResultType() ) {
+                case ResultWas::ThrewException:
+                    m_xml.scopedElement( "Exception" )
+                        .writeAttribute( "filename", assertionResult.getSourceInfo().file )
+                        .writeAttribute( "line", assertionResult.getSourceInfo().line )
+                        .writeText( assertionResult.getMessage() );
+                    break;
+                case ResultWas::FatalErrorCondition:
+                    m_xml.scopedElement( "Fatal Error Condition" )
+                        .writeAttribute( "filename", assertionResult.getSourceInfo().file )
+                        .writeAttribute( "line", assertionResult.getSourceInfo().line )
+                        .writeText( assertionResult.getMessage() );
+                    break;
+                case ResultWas::Info:
+                    m_xml.scopedElement( "Info" )
+                        .writeText( assertionResult.getMessage() );
+                    break;
+                case ResultWas::Warning:
+                    // Warning will already have been written
+                    break;
+                case ResultWas::ExplicitFailure:
+                    m_xml.scopedElement( "Failure" )
+                        .writeText( assertionResult.getMessage() );
+                    break;
+                default:
+                    break;
+            }
+
+            if( assertionResult.hasExpression() )
+                m_xml.endElement();
+
+            return true;
+        }
+
+        virtual void 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 );
+
+                if ( m_config->showDurations() == ShowDurations::Always )
+                    e.writeAttribute( "durationInSeconds", sectionStats.durationInSeconds );
+
+                m_xml.endElement();
+            }
+        }
+
+        virtual void 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() );
+
+            m_xml.endElement();
+        }
+
+        virtual void 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.endElement();
+        }
+
+        virtual void 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.endElement();
+        }
+
+    private:
+        Timer m_testCaseTimer;
+        XmlWriter m_xml;
+        int m_sectionDepth;
+    };
+
+     INTERNAL_CATCH_REGISTER_REPORTER( "xml", XmlReporter )
+
+} // end namespace Catch
+
+// #included from: ../reporters/catch_reporter_junit.hpp
+#define TWOBLUECUBES_CATCH_REPORTER_JUNIT_HPP_INCLUDED
+
+#include <assert.h>
+
+namespace Catch {
+
+    class JunitReporter : public CumulativeReporterBase {
+    public:
+        JunitReporter( ReporterConfig const& _config )
+        :   CumulativeReporterBase( _config ),
+            xml( _config.stream() )
+        {}
+
+        ~JunitReporter();
+
+        static std::string getDescription() {
+            return "Reports test results in an XML format that looks like Ant's junitreport target";
+        }
+
+        virtual void noMatchingTestCases( std::string const& /*spec*/ ) {}
+
+        virtual ReporterPreferences getPreferences() const {
+            ReporterPreferences prefs;
+            prefs.shouldRedirectStdOut = true;
+            return prefs;
+        }
+
+        virtual void testRunStarting( TestRunInfo const& runInfo ) {
+            CumulativeReporterBase::testRunStarting( runInfo );
+            xml.startElement( "testsuites" );
+        }
+
+        virtual void testGroupStarting( GroupInfo const& groupInfo ) {
+            suiteTimer.start();
+            stdOutForSuite.str("");
+            stdErrForSuite.str("");
+            unexpectedExceptions = 0;
+            CumulativeReporterBase::testGroupStarting( groupInfo );
+        }
+
+        virtual bool assertionEnded( AssertionStats const& assertionStats ) {
+            if( assertionStats.assertionResult.getResultType() == ResultWas::ThrewException )
+                unexpectedExceptions++;
+            return CumulativeReporterBase::assertionEnded( assertionStats );
+        }
+
+        virtual void testCaseEnded( TestCaseStats const& testCaseStats ) {
+            stdOutForSuite << testCaseStats.stdOut;
+            stdErrForSuite << testCaseStats.stdErr;
+            CumulativeReporterBase::testCaseEnded( testCaseStats );
+        }
+
+        virtual void testGroupEnded( TestGroupStats const& testGroupStats ) {
+            double suiteTime = suiteTimer.getElapsedSeconds();
+            CumulativeReporterBase::testGroupEnded( testGroupStats );
+            writeGroup( *m_testGroups.back(), suiteTime );
+        }
+
+        virtual void testRunEndedCumulative() {
+            xml.endElement();
+        }
+
+        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", "tbd" ); // !TBD
+
+            // Write test cases
+            for( TestGroupNode::ChildNodes::const_iterator
+                    it = groupNode.children.begin(), itEnd = groupNode.children.end();
+                    it != itEnd;
+                    ++it )
+                writeTestCase( **it );
+
+            xml.scopedElement( "system-out" ).writeText( trim( stdOutForSuite.str() ), false );
+            xml.scopedElement( "system-err" ).writeText( trim( stdErrForSuite.str() ), false );
+        }
+
+        void writeTestCase( TestCaseNode const& testCaseNode ) {
+            TestCaseStats const& stats = testCaseNode.value;
+
+            // 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();
+
+            std::string className = stats.testInfo.className;
+
+            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;
+                }
+
+                XmlWriter::ScopedElement e = xml.scopedElement( elementName );
+
+                xml.writeAttribute( "message", result.getExpandedExpression() );
+                xml.writeAttribute( "type", result.getTestMacroName() );
+
+                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";
+
+                oss << "at " << result.getSourceInfo();
+                xml.writeText( oss.str(), false );
+            }
+        }
+
+        XmlWriter xml;
+        Timer suiteTimer;
+        std::ostringstream stdOutForSuite;
+        std::ostringstream stdErrForSuite;
+        unsigned int unexpectedExceptions;
+    };
+
+    INTERNAL_CATCH_REGISTER_REPORTER( "junit", JunitReporter )
+
+} // end namespace Catch
+
+// #included from: ../reporters/catch_reporter_console.hpp
+#define TWOBLUECUBES_CATCH_REPORTER_CONSOLE_HPP_INCLUDED
+
+namespace Catch {
+
+    struct ConsoleReporter : StreamingReporterBase {
+        ConsoleReporter( ReporterConfig const& _config )
+        :   StreamingReporterBase( _config ),
+            m_headerPrinted( false )
+        {}
+
+        virtual ~ConsoleReporter();
+        static std::string getDescription() {
+            return "Reports test results as plain lines of text";
+        }
+        virtual ReporterPreferences getPreferences() const {
+            ReporterPreferences prefs;
+            prefs.shouldRedirectStdOut = false;
+            return prefs;
+        }
+
+        virtual void noMatchingTestCases( std::string const& spec ) {
+            stream << "No test cases matched '" << spec << "'" << std::endl;
+        }
+
+        virtual void assertionStarting( AssertionInfo const& ) {
+        }
+
+        virtual bool assertionEnded( AssertionStats const& _assertionStats ) {
+            AssertionResult const& result = _assertionStats.assertionResult;
+
+            bool printInfoMessages = true;
+
+            // 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;
+            }
+
+            lazyPrint();
+
+            AssertionPrinter printer( stream, _assertionStats, printInfoMessages );
+            printer.print();
+            stream << std::endl;
+            return true;
+        }
+
+        virtual void sectionStarting( SectionInfo const& _sectionInfo ) {
+            m_headerPrinted = false;
+            StreamingReporterBase::sectionStarting( _sectionInfo );
+        }
+        virtual void sectionEnded( SectionStats const& _sectionStats ) {
+            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( m_headerPrinted ) {
+                if( m_config->showDurations() == ShowDurations::Always )
+                    stream << "Completed in " << _sectionStats.durationInSeconds << "s" << std::endl;
+                m_headerPrinted = false;
+            }
+            else {
+                if( m_config->showDurations() == ShowDurations::Always )
+                    stream << _sectionStats.sectionInfo.name << " completed in " << _sectionStats.durationInSeconds << "s" << std::endl;
+            }
+            StreamingReporterBase::sectionEnded( _sectionStats );
+        }
+
+        virtual void testCaseEnded( TestCaseStats const& _testCaseStats ) {
+            StreamingReporterBase::testCaseEnded( _testCaseStats );
+            m_headerPrinted = false;
+        }
+        virtual void 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 );
+        }
+        virtual void testRunEnded( TestRunStats const& _testRunStats ) {
+            printTotalsDivider( _testRunStats.totals );
+            printTotals( _testRunStats.totals );
+            stream << std::endl;
+            StreamingReporterBase::testRunEnded( _testRunStats );
+        }
+
+    private:
+
+        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 message";
+                        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 ) {
+                    if( result.isOk() )
+                        stream << "\n";
+                    printResultType();
+                    printOriginalExpression();
+                    printReconstructedExpression();
+                }
+                else {
+                    stream << "\n";
+                }
+                printMessage();
+            }
+
+        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() << ": ";
+            }
+
+            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;
+        };
+
+        void lazyPrint() {
+
+            if( !currentTestRunInfo.used )
+                lazyPrintRunInfo();
+            if( !currentGroupInfo.used )
+                lazyPrintGroupInfo();
+
+            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";
+
+            if( m_config->rngSeed() != 0 )
+                stream << "Randomness seeded to: " << m_config->rngSeed() << "\n\n";
+
+            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 );
+
+            if( m_sectionStack.size() > 1 ) {
+                Colour colourGuard( Colour::Headers );
+
+                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 );
+            }
+
+            SourceLineInfo lineInfo = m_sectionStack.front().lineInfo;
+
+            if( !lineInfo.empty() ){
+                stream << getLineOfChars<'-'>() << "\n";
+                Colour colourGuard( Colour::FileName );
+                stream << lineInfo << "\n";
+            }
+            stream << getLineOfChars<'.'>() << "\n" << std::endl;
+        }
+
+        void printClosedHeader( std::string const& _name ) {
+            printOpenHeader( _name );
+            stream << getLineOfChars<'.'>() << "\n";
+        }
+        void printOpenHeader( std::string const& _name ) {
+            stream  << getLineOfChars<'-'>() << "\n";
+            {
+                Colour colourGuard( Colour::Headers );
+                printHeaderString( _name );
+            }
+        }
+
+        // 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";
+        }
+
+        struct SummaryColumn {
+
+            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;
+            }
+
+            std::string label;
+            Colour::Code colour;
+            std::vector<std::string> rows;
+
+        };
+
+        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.assertions.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 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";
+        }
+
+        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;
+        }
+        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";
+        }
+        void printSummaryDivider() {
+            stream << getLineOfChars<'-'>() << "\n";
+        }
+
+    private:
+        bool m_headerPrinted;
+    };
+
+    INTERNAL_CATCH_REGISTER_REPORTER( "console", ConsoleReporter )
+
+} // end namespace Catch
+
+// #included from: ../reporters/catch_reporter_compact.hpp
+#define TWOBLUECUBES_CATCH_REPORTER_COMPACT_HPP_INCLUDED
+
+namespace Catch {
+
+    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";
+        }
+
+        virtual ReporterPreferences getPreferences() const {
+            ReporterPreferences prefs;
+            prefs.shouldRedirectStdOut = false;
+            return prefs;
+        }
+
+        virtual void noMatchingTestCases( std::string const& spec ) {
+            stream << "No test cases matched '" << spec << "'" << std::endl;
+        }
+
+        virtual void assertionStarting( AssertionInfo const& ) {
+        }
+
+        virtual bool assertionEnded( AssertionStats const& _assertionStats ) {
+            AssertionResult const& result = _assertionStats.assertionResult;
+
+            bool printInfoMessages = true;
+
+            // 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;
+            }
+
+            AssertionPrinter printer( stream, _assertionStats, printInfoMessages );
+            printer.print();
+
+            stream << std::endl;
+            return true;
+        }
+
+        virtual void testRunEnded( TestRunStats const& _testRunStats ) {
+            printTotals( _testRunStats.totals );
+            stream << "\n" << std::endl;
+            StreamingReporterBase::testRunEnded( _testRunStats );
+        }
+
+    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
+
+            static Colour::Code dimColour() { return Colour::FileName; }
+
+#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
+
+            void printSourceInfo() const {
+                Colour colourGuard( Colour::FileName );
+                stream << result.getSourceInfo() << ":";
+            }
+
+            void printResultType( Colour::Code colour, std::string passOrFail ) const {
+                if( !passOrFail.empty() ) {
+                    {
+                        Colour colourGuard( colour );
+                        stream << " " << passOrFail;
+                    }
+                    stream << ":";
+                }
+            }
+
+            void printIssue( std::string issue ) const {
+                stream << " " << issue;
+            }
+
+            void printExpressionWas() {
+                if( result.hasExpression() ) {
+                    stream << ";";
+                    {
+                        Colour colour( dimColour() );
+                        stream << " expression was:";
+                    }
+                    printOriginalExpression();
+                }
+            }
+
+            void printOriginalExpression() const {
+                if( result.hasExpression() ) {
+                    stream << " " << result.getExpression();
+                }
+            }
+
+            void printReconstructedExpression() const {
+                if( result.hasExpandedExpression() ) {
+                    {
+                        Colour colour( dimColour() );
+                        stream << " for: ";
+                    }
+                    stream << result.getExpandedExpression();
+                }
+            }
+
+            void printMessage() {
+                if ( itMessage != messages.end() ) {
+                    stream << " '" << itMessage->message << "'";
+                    ++itMessage;
+                }
+            }
+
+            void printRemainingMessages( Colour::Code colour = dimColour() ) {
+                if ( itMessage == messages.end() )
+                    return;
+
+                // 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 ) );
+
+                {
+                    Colour colourGuard( colour );
+                    stream << " with " << pluralise( N, "message" ) << ":";
+                }
+
+                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";
+                        }
+                    }
+                }
+            }
+
+        private:
+            std::ostream& stream;
+            AssertionStats const& stats;
+            AssertionResult const& result;
+            std::vector<MessageInfo> messages;
+            std::vector<MessageInfo>::const_iterator itMessage;
+            bool printInfoMessages;
+        };
+
+        // 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 ? "" : 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 ) : "";
+                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" ) << ".";
+            }
+        }
+    };
+
+    INTERNAL_CATCH_REGISTER_REPORTER( "compact", CompactReporter )
+
+} // end namespace Catch
+
+namespace Catch {
+    NonCopyable::~NonCopyable() {}
+    IShared::~IShared() {}
+    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() {}
+    TestSpec::Pattern::~Pattern() {}
+    TestSpec::NamePattern::~NamePattern() {}
+    TestSpec::TagPattern::~TagPattern() {}
+    TestSpec::ExcludedPattern::~ExcludedPattern() {}
+
+    Matchers::Impl::StdString::Equals::~Equals() {}
+    Matchers::Impl::StdString::Contains::~Contains() {}
+    Matchers::Impl::StdString::StartsWith::~StartsWith() {}
+    Matchers::Impl::StdString::EndsWith::~EndsWith() {}
+
+    void Config::dummy() {}
+}
+
+#ifdef __clang__
+#pragma clang diagnostic pop
+#endif
+
+#endif
+
+#ifdef CATCH_CONFIG_MAIN
+// #included from: internal/catch_default_main.hpp
+#define TWOBLUECUBES_CATCH_DEFAULT_MAIN_HPP_INCLUDED
+
+#ifndef __OBJC__
+
+// Standard C/C++ main entry point
+int main (int argc, char * const argv[]) {
+    return Catch::Session().run( argc, argv );
+}
+
+#else // __OBJC__
+
+// Objective-C entry point
+int main (int argc, char * const argv[]) {
+#if !CATCH_ARC_ENABLED
+    NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];
+#endif
+
+    Catch::registerTestMethods();
+    int result = Catch::Session().run( argc, (char* const*)argv );
+
+#if !CATCH_ARC_ENABLED
+    [pool drain];
+#endif
+
+    return result;
+}
+
+#endif // __OBJC__
+
+#endif
+
+#ifdef CLARA_CONFIG_MAIN_NOT_DEFINED
+#  undef CLARA_CONFIG_MAIN
+#endif
+
+//////
+
+// If this config identifier is defined then all CATCH macros are prefixed with CATCH_
+#ifdef CATCH_CONFIG_PREFIX_ALL
+
+#define CATCH_REQUIRE( expr ) INTERNAL_CATCH_TEST( expr, Catch::ResultDisposition::Normal, "CATCH_REQUIRE" )
+#define CATCH_REQUIRE_FALSE( expr ) INTERNAL_CATCH_TEST( expr, Catch::ResultDisposition::Normal | Catch::ResultDisposition::FalseTest, "CATCH_REQUIRE_FALSE" )
+
+#define CATCH_REQUIRE_THROWS( expr ) INTERNAL_CATCH_THROWS( expr, Catch::ResultDisposition::Normal, "CATCH_REQUIRE_THROWS" )
+#define CATCH_REQUIRE_THROWS_AS( expr, exceptionType ) INTERNAL_CATCH_THROWS_AS( expr, exceptionType, Catch::ResultDisposition::Normal, "CATCH_REQUIRE_THROWS_AS" )
+#define CATCH_REQUIRE_NOTHROW( expr ) INTERNAL_CATCH_NO_THROW( expr, Catch::ResultDisposition::Normal, "CATCH_REQUIRE_NOTHROW" )
+
+#define CATCH_CHECK( expr ) INTERNAL_CATCH_TEST( expr, Catch::ResultDisposition::ContinueOnFailure, "CATCH_CHECK" )
+#define CATCH_CHECK_FALSE( expr ) INTERNAL_CATCH_TEST( expr, Catch::ResultDisposition::ContinueOnFailure | Catch::ResultDisposition::FalseTest, "CATCH_CHECK_FALSE" )
+#define CATCH_CHECKED_IF( expr ) INTERNAL_CATCH_IF( expr, Catch::ResultDisposition::ContinueOnFailure, "CATCH_CHECKED_IF" )
+#define CATCH_CHECKED_ELSE( expr ) INTERNAL_CATCH_ELSE( expr, Catch::ResultDisposition::ContinueOnFailure, "CATCH_CHECKED_ELSE" )
+#define CATCH_CHECK_NOFAIL( expr ) INTERNAL_CATCH_TEST( expr, Catch::ResultDisposition::ContinueOnFailure | Catch::ResultDisposition::SuppressFail, "CATCH_CHECK_NOFAIL" )
+
+#define CATCH_CHECK_THROWS( expr )  INTERNAL_CATCH_THROWS( expr, Catch::ResultDisposition::ContinueOnFailure, "CATCH_CHECK_THROWS" )
+#define CATCH_CHECK_THROWS_AS( expr, exceptionType ) INTERNAL_CATCH_THROWS_AS( expr, exceptionType, Catch::ResultDisposition::ContinueOnFailure, "CATCH_CHECK_THROWS_AS" )
+#define CATCH_CHECK_NOTHROW( expr ) INTERNAL_CATCH_NO_THROW( expr, Catch::ResultDisposition::ContinueOnFailure, "CATCH_CHECK_NOTHROW" )
+
+#define CHECK_THAT( arg, matcher ) INTERNAL_CHECK_THAT( arg, matcher, Catch::ResultDisposition::ContinueOnFailure, "CATCH_CHECK_THAT" )
+#define CATCH_REQUIRE_THAT( arg, matcher ) INTERNAL_CHECK_THAT( arg, matcher, Catch::ResultDisposition::Normal, "CATCH_REQUIRE_THAT" )
+
+#define CATCH_INFO( msg ) INTERNAL_CATCH_INFO( msg, "CATCH_INFO" )
+#define CATCH_WARN( msg ) INTERNAL_CATCH_MSG( Catch::ResultWas::Warning, Catch::ResultDisposition::ContinueOnFailure, "CATCH_WARN", msg )
+#define CATCH_SCOPED_INFO( msg ) INTERNAL_CATCH_INFO( msg, "CATCH_INFO" )
+#define CATCH_CAPTURE( msg ) INTERNAL_CATCH_INFO( #msg " := " << msg, "CATCH_CAPTURE" )
+#define CATCH_SCOPED_CAPTURE( msg ) INTERNAL_CATCH_INFO( #msg " := " << msg, "CATCH_CAPTURE" )
+
+#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_SECTION( ... ) INTERNAL_CATCH_SECTION( __VA_ARGS__ )
+    #define CATCH_FAIL( ... ) INTERNAL_CATCH_MSG( Catch::ResultWas::ExplicitFailure, Catch::ResultDisposition::Normal, "CATCH_FAIL", __VA_ARGS__ )
+    #define CATCH_SUCCEED( ... ) INTERNAL_CATCH_MSG( Catch::ResultWas::Ok, Catch::ResultDisposition::ContinueOnFailure, "CATCH_SUCCEED", __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_SECTION( name, description ) INTERNAL_CATCH_SECTION( name, description )
+    #define CATCH_FAIL( msg ) INTERNAL_CATCH_MSG( Catch::ResultWas::ExplicitFailure, Catch::ResultDisposition::Normal, "CATCH_FAIL", msg )
+    #define CATCH_SUCCEED( msg ) INTERNAL_CATCH_MSG( Catch::ResultWas::Ok, Catch::ResultDisposition::ContinueOnFailure, "CATCH_SUCCEED", msg )
+#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 )
+
+// "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( "Given: " desc, "" )
+#define CATCH_WHEN( desc )     CATCH_SECTION( " When: " desc, "" )
+#define CATCH_AND_WHEN( desc ) CATCH_SECTION( "  And: " desc, "" )
+#define CATCH_THEN( desc )     CATCH_SECTION( " Then: " desc, "" )
+#define CATCH_AND_THEN( desc ) CATCH_SECTION( "  And: " desc, "" )
+
+// If CATCH_CONFIG_PREFIX_ALL is not defined then the CATCH_ prefix is not required
+#else
+
+#define REQUIRE( expr ) INTERNAL_CATCH_TEST( expr, Catch::ResultDisposition::Normal, "REQUIRE" )
+#define REQUIRE_FALSE( expr ) INTERNAL_CATCH_TEST( expr, Catch::ResultDisposition::Normal | Catch::ResultDisposition::FalseTest, "REQUIRE_FALSE" )
+
+#define REQUIRE_THROWS( expr ) INTERNAL_CATCH_THROWS( expr, Catch::ResultDisposition::Normal, "REQUIRE_THROWS" )
+#define REQUIRE_THROWS_AS( expr, exceptionType ) INTERNAL_CATCH_THROWS_AS( expr, exceptionType, Catch::ResultDisposition::Normal, "REQUIRE_THROWS_AS" )
+#define REQUIRE_NOTHROW( expr ) INTERNAL_CATCH_NO_THROW( expr, Catch::ResultDisposition::Normal, "REQUIRE_NOTHROW" )
+
+#define CHECK( expr ) INTERNAL_CATCH_TEST( expr, Catch::ResultDisposition::ContinueOnFailure, "CHECK" )
+#define CHECK_FALSE( expr ) INTERNAL_CATCH_TEST( expr, Catch::ResultDisposition::ContinueOnFailure | Catch::ResultDisposition::FalseTest, "CHECK_FALSE" )
+#define CHECKED_IF( expr ) INTERNAL_CATCH_IF( expr, Catch::ResultDisposition::ContinueOnFailure, "CHECKED_IF" )
+#define CHECKED_ELSE( expr ) INTERNAL_CATCH_ELSE( expr, Catch::ResultDisposition::ContinueOnFailure, "CHECKED_ELSE" )
+#define CHECK_NOFAIL( expr ) INTERNAL_CATCH_TEST( expr, Catch::ResultDisposition::ContinueOnFailure | Catch::ResultDisposition::SuppressFail, "CHECK_NOFAIL" )
+
+#define CHECK_THROWS( expr )  INTERNAL_CATCH_THROWS( expr, Catch::ResultDisposition::ContinueOnFailure, "CHECK_THROWS" )
+#define CHECK_THROWS_AS( expr, exceptionType ) INTERNAL_CATCH_THROWS_AS( expr, exceptionType, Catch::ResultDisposition::ContinueOnFailure, "CHECK_THROWS_AS" )
+#define CHECK_NOTHROW( expr ) INTERNAL_CATCH_NO_THROW( expr, Catch::ResultDisposition::ContinueOnFailure, "CHECK_NOTHROW" )
+
+#define CHECK_THAT( arg, matcher ) INTERNAL_CHECK_THAT( arg, matcher, Catch::ResultDisposition::ContinueOnFailure, "CHECK_THAT" )
+#define REQUIRE_THAT( arg, matcher ) INTERNAL_CHECK_THAT( arg, matcher, Catch::ResultDisposition::Normal, "REQUIRE_THAT" )
+
+#define INFO( msg ) INTERNAL_CATCH_INFO( msg, "INFO" )
+#define WARN( msg ) INTERNAL_CATCH_MSG( Catch::ResultWas::Warning, Catch::ResultDisposition::ContinueOnFailure, "WARN", msg )
+#define SCOPED_INFO( msg ) INTERNAL_CATCH_INFO( msg, "INFO" )
+#define CAPTURE( msg ) INTERNAL_CATCH_INFO( #msg " := " << msg, "CAPTURE" )
+#define SCOPED_CAPTURE( msg ) INTERNAL_CATCH_INFO( #msg " := " << msg, "CAPTURE" )
+
+#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 SECTION( ... ) INTERNAL_CATCH_SECTION( __VA_ARGS__ )
+    #define FAIL( ... ) INTERNAL_CATCH_MSG( Catch::ResultWas::ExplicitFailure, Catch::ResultDisposition::Normal, "FAIL", __VA_ARGS__ )
+    #define SUCCEED( ... ) INTERNAL_CATCH_MSG( Catch::ResultWas::Ok, Catch::ResultDisposition::ContinueOnFailure, "SUCCEED", __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 SECTION( name, description ) INTERNAL_CATCH_SECTION( name, description )
+    #define FAIL( msg ) INTERNAL_CATCH_MSG( Catch::ResultWas::ExplicitFailure, Catch::ResultDisposition::Normal, "FAIL", msg )
+    #define SUCCEED( msg ) INTERNAL_CATCH_MSG( Catch::ResultWas::Ok, Catch::ResultDisposition::ContinueOnFailure, "SUCCEED", msg )
+#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 )
+
+#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__ )
+#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 )
+#endif
+#define GIVEN( desc )    SECTION( "   Given: " desc, "" )
+#define WHEN( desc )     SECTION( "    When: " desc, "" )
+#define AND_WHEN( desc ) SECTION( "And when: " desc, "" )
+#define THEN( desc )     SECTION( "    Then: " desc, "" )
+#define AND_THEN( desc ) SECTION( "     And: " desc, "" )
+
+using Catch::Detail::Approx;
+
+// #included from: internal/catch_reenable_warnings.h
+
+#define TWOBLUECUBES_CATCH_REENABLE_WARNINGS_H_INCLUDED
+
+#ifdef __clang__
+#    ifdef __ICC // icpc defines the __clang__ macro
+#        pragma warning(pop)
+#    else
+#        pragma clang diagnostic pop
+#    endif
+#elif defined __GNUC__
+#    pragma GCC diagnostic pop
+#endif
+
+#endif // TWOBLUECUBES_SINGLE_INCLUDE_CATCH_HPP_INCLUDED
+
diff --git a/test/include/test.hpp b/test/include/test.hpp
new file mode 100644
index 0000000..a6a0b0c
--- /dev/null
+++ b/test/include/test.hpp
@@ -0,0 +1,17 @@
+
+#include <catch.hpp>
+
+#include <stdexcept>
+// Define pbf_assert() to throw this error. This allows the tests to check that
+// the assert fails.
+struct assert_error : public std::runtime_error {
+    assert_error(const char* what_arg) : std::runtime_error(what_arg) {
+    }
+};
+#define pbf_assert(x) if (!(x)) { throw(assert_error(#x)); }
+
+#include <protozero/pbf_reader.hpp>
+#include <protozero/pbf_writer.hpp>
+
+extern std::string load_data(const std::string& filename);
+
diff --git a/test/include/testcase.hpp b/test/include/testcase.hpp
new file mode 100644
index 0000000..1005061
--- /dev/null
+++ b/test/include/testcase.hpp
@@ -0,0 +1,21 @@
+#ifndef TESTCASE_HPP
+#define TESTCASE_HPP
+
+#include <cassert>
+#include <string>
+#include <fstream>
+#include <limits>
+
+template <class T>
+std::string write_to_file(const T& msg, const char* filename) {
+    std::string out;
+
+    msg.SerializeToString(&out);
+    std::ofstream d(filename);
+    assert(d.is_open());
+    d << out;
+
+    return out;
+}
+
+#endif // TESTCASE_HPP
diff --git a/test/t/.gitignore b/test/t/.gitignore
new file mode 100644
index 0000000..1799818
--- /dev/null
+++ b/test/t/.gitignore
@@ -0,0 +1,6 @@
+testcase
+testcase.pb.cc
+testcase.pb.h
+testcase.pb.o
+test_cases.o
+writer_test_cases.o
diff --git a/test/t/basic/test_cases.cpp b/test/t/basic/test_cases.cpp
new file mode 100644
index 0000000..89c342c
--- /dev/null
+++ b/test/t/basic/test_cases.cpp
@@ -0,0 +1,47 @@
+
+#include <test.hpp>
+
+TEST_CASE("basic") {
+
+    SECTION("default constructed pbf message is okay") {
+        protozero::pbf_reader item;
+
+        REQUIRE(item.length() == 0);
+        REQUIRE(!item); // test operator bool()
+        REQUIRE(!item.next());
+    }
+
+    SECTION("empty buffer is okay") {
+        std::string buffer;
+        protozero::pbf_reader item(buffer);
+
+        REQUIRE(item.length() == 0);
+        REQUIRE(!item); // test operator bool()
+        REQUIRE(!item.next());
+    }
+
+    SECTION("check every possible value for single byte in buffer") {
+        char buffer[1];
+        for (int i = 0; i <= 255; ++i) {
+            *buffer = static_cast<char>(i);
+            protozero::pbf_reader item(buffer, 1);
+
+            REQUIRE(item.length() == 1);
+            REQUIRE(!!item); // test operator bool()
+            REQUIRE_THROWS({
+                item.next();
+                item.skip();
+            });
+        }
+    }
+
+    SECTION("illegal wire type") {
+        char buffer[1] = { 1 << 3 | 7 };
+
+        protozero::pbf_reader item(buffer, 1);
+        REQUIRE(item.next());
+        REQUIRE_THROWS_AS(item.skip(), protozero::unknown_pbf_wire_type_exception);
+    }
+
+}
+
diff --git a/test/t/bool/data-also-true.pbf b/test/t/bool/data-also-true.pbf
new file mode 100644
index 0000000..e19a122
--- /dev/null
+++ b/test/t/bool/data-also-true.pbf
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/test/t/bool/data-false.pbf b/test/t/bool/data-false.pbf
new file mode 100644
index 0000000..268720a
Binary files /dev/null and b/test/t/bool/data-false.pbf differ
diff --git a/test/t/bool/data-still-true.pbf b/test/t/bool/data-still-true.pbf
new file mode 100644
index 0000000..e19a122
--- /dev/null
+++ b/test/t/bool/data-still-true.pbf
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/test/t/bool/data-true.pbf b/test/t/bool/data-true.pbf
new file mode 100644
index 0000000..e19a122
--- /dev/null
+++ b/test/t/bool/data-true.pbf
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/test/t/bool/test_cases.cpp b/test/t/bool/test_cases.cpp
new file mode 100644
index 0000000..06cc02c
--- /dev/null
+++ b/test/t/bool/test_cases.cpp
@@ -0,0 +1,64 @@
+
+#include <test.hpp>
+
+TEST_CASE("read bool field") {
+
+    SECTION("false") {
+        std::string buffer = load_data("bool/data-false");
+
+        protozero::pbf_reader item(buffer);
+
+        REQUIRE(item.next());
+        REQUIRE(!item.get_bool());
+        REQUIRE(!item.next());
+    }
+
+    SECTION("true") {
+        std::string buffer = load_data("bool/data-true");
+
+        protozero::pbf_reader item(buffer);
+
+        REQUIRE(item.next());
+        REQUIRE(item.get_bool());
+        REQUIRE(!item.next());
+    }
+
+    SECTION("also true") {
+        std::string buffer = load_data("bool/data-also-true");
+
+        protozero::pbf_reader item(buffer);
+
+        REQUIRE(item.next());
+        REQUIRE(item.get_bool());
+        REQUIRE(!item.next());
+    }
+
+    SECTION("still true") {
+        std::string buffer = load_data("bool/data-still-true");
+
+        protozero::pbf_reader item(buffer);
+
+        REQUIRE(item.next());
+        REQUIRE(item.get_bool());
+        REQUIRE(!item.next());
+    }
+
+}
+
+TEST_CASE("write bool field") {
+
+    std::string buffer;
+    protozero::pbf_writer pw(buffer);
+
+    SECTION("false") {
+        pw.add_bool(1, false);
+        REQUIRE(buffer == load_data("bool/data-false"));
+    }
+
+    SECTION("true") {
+        pw.add_bool(1, true);
+        REQUIRE(buffer == load_data("bool/data-true"));
+    }
+
+}
+
diff --git a/test/t/bool/testcase.cpp b/test/t/bool/testcase.cpp
new file mode 100644
index 0000000..3ad5083
--- /dev/null
+++ b/test/t/bool/testcase.cpp
@@ -0,0 +1,20 @@
+
+#include <testcase.hpp>
+#include "testcase.pb.h"
+
+int main(int c, char *argv[]) {
+    TestBoolean::Test msg;
+
+    msg.set_b(0);
+    write_to_file(msg, "data-false.pbf");
+
+    msg.set_b(1);
+    write_to_file(msg, "data-true.pbf");
+
+    msg.set_b(2);
+    write_to_file(msg, "data-also-true.pbf");
+
+    msg.set_b(127);
+    write_to_file(msg, "data-still-true.pbf");
+}
+
diff --git a/test/t/bool/testcase.proto b/test/t/bool/testcase.proto
new file mode 100644
index 0000000..3c9fefa
--- /dev/null
+++ b/test/t/bool/testcase.proto
@@ -0,0 +1,11 @@
+
+option optimize_for = LITE_RUNTIME;
+
+package TestBoolean;
+
+message Test {
+
+    required bool b = 1;
+
+}
+
diff --git a/test/t/bool/writer_test_cases.cpp b/test/t/bool/writer_test_cases.cpp
new file mode 100644
index 0000000..8494b73
--- /dev/null
+++ b/test/t/bool/writer_test_cases.cpp
@@ -0,0 +1,32 @@
+
+#include <string>
+
+#include <test.hpp> // IWYU pragma: keep
+
+#include "test/t/bool/testcase.pb.h"
+
+TEST_CASE("write bool field and check with libprotobuf") {
+
+    std::string buffer;
+    protozero::pbf_writer pw(buffer);
+
+    TestBoolean::Test msg;
+
+    SECTION("false") {
+        pw.add_bool(1, false);
+
+        msg.ParseFromString(buffer);
+
+        REQUIRE(!msg.b());
+    }
+
+    SECTION("true") {
+        pw.add_bool(1, true);
+
+        msg.ParseFromString(buffer);
+
+        REQUIRE(msg.b());
+    }
+
+}
+
diff --git a/test/t/bytes/data-binary.pbf b/test/t/bytes/data-binary.pbf
new file mode 100644
index 0000000..f6aaef7
--- /dev/null
+++ b/test/t/bytes/data-binary.pbf
@@ -0,0 +1,2 @@
+
+
\ No newline at end of file
diff --git a/test/t/bytes/data-empty.pbf b/test/t/bytes/data-empty.pbf
new file mode 100644
index 0000000..59f144e
Binary files /dev/null and b/test/t/bytes/data-empty.pbf differ
diff --git a/test/t/bytes/data-one.pbf b/test/t/bytes/data-one.pbf
new file mode 100644
index 0000000..2764cf5
--- /dev/null
+++ b/test/t/bytes/data-one.pbf
@@ -0,0 +1,2 @@
+
+x
\ No newline at end of file
diff --git a/test/t/bytes/data-string.pbf b/test/t/bytes/data-string.pbf
new file mode 100644
index 0000000..d7369a7
--- /dev/null
+++ b/test/t/bytes/data-string.pbf
@@ -0,0 +1,2 @@
+
+foobar
\ No newline at end of file
diff --git a/test/t/bytes/test_cases.cpp b/test/t/bytes/test_cases.cpp
new file mode 100644
index 0000000..57791f1
--- /dev/null
+++ b/test/t/bytes/test_cases.cpp
@@ -0,0 +1,94 @@
+
+#include <test.hpp>
+
+TEST_CASE("read bytes field") {
+
+    SECTION("empty") {
+        std::string buffer = load_data("bytes/data-empty");
+
+        protozero::pbf_reader item(buffer);
+
+        REQUIRE(item.next());
+        REQUIRE(item.get_string() == "");
+        REQUIRE(!item.next());
+    }
+
+    SECTION("one") {
+        std::string buffer = load_data("bytes/data-one");
+
+        protozero::pbf_reader item(buffer);
+
+        REQUIRE(item.next());
+        REQUIRE(item.get_string() == "x");
+        REQUIRE(!item.next());
+    }
+
+    SECTION("string") {
+        std::string buffer = load_data("bytes/data-string");
+
+        protozero::pbf_reader item(buffer);
+
+        REQUIRE(item.next());
+        REQUIRE(item.get_string() == "foobar");
+        REQUIRE(!item.next());
+    }
+
+    SECTION("binary") {
+        std::string buffer = load_data("bytes/data-binary");
+
+        protozero::pbf_reader item(buffer);
+
+        REQUIRE(item.next());
+        std::string data = item.get_string();
+        REQUIRE(data.size() == 3);
+        REQUIRE(data[0] == char(1));
+        REQUIRE(data[1] == char(2));
+        REQUIRE(data[2] == char(3));
+        REQUIRE(!item.next());
+    }
+
+    SECTION("end_of_buffer") {
+        std::string buffer = load_data("bytes/data-binary");
+
+        for (size_t i=1; i < buffer.size(); ++i) {
+            protozero::pbf_reader item(buffer.data(), i);
+            REQUIRE(item.next());
+            REQUIRE_THROWS_AS(item.get_string(), protozero::end_of_buffer_exception);
+        }
+    }
+
+}
+
+TEST_CASE("write bytes field") {
+
+    std::string buffer;
+    protozero::pbf_writer pw(buffer);
+
+    SECTION("empty") {
+        pw.add_string(1, "");
+        REQUIRE(buffer == load_data("bytes/data-empty"));
+    }
+
+    SECTION("one") {
+        pw.add_string(1, "x");
+        REQUIRE(buffer == load_data("bytes/data-one"));
+    }
+
+    SECTION("string") {
+        pw.add_string(1, "foobar");
+        REQUIRE(buffer == load_data("bytes/data-string"));
+    }
+
+    SECTION("binary") {
+        std::string data;
+        data.append(1, char(1));
+        data.append(1, char(2));
+        data.append(1, char(3));
+
+        pw.add_string(1, data);
+
+        REQUIRE(buffer == load_data("bytes/data-binary"));
+    }
+
+}
+
diff --git a/test/t/bytes/testcase.cpp b/test/t/bytes/testcase.cpp
new file mode 100644
index 0000000..32e37b8
--- /dev/null
+++ b/test/t/bytes/testcase.cpp
@@ -0,0 +1,24 @@
+
+#include <testcase.hpp>
+#include "testcase.pb.h"
+
+int main(int c, char *argv[]) {
+    TestBytes::Test msg;
+
+    msg.set_s("");
+    write_to_file(msg, "data-empty.pbf");
+
+    msg.set_s("x");
+    write_to_file(msg, "data-one.pbf");
+
+    msg.set_s("foobar");
+    write_to_file(msg, "data-string.pbf");
+
+    std::string data;
+    data.append(1, char(1));
+    data.append(1, char(2));
+    data.append(1, char(3));
+    msg.set_s(data);
+    write_to_file(msg, "data-binary.pbf");
+}
+
diff --git a/test/t/bytes/testcase.proto b/test/t/bytes/testcase.proto
new file mode 100644
index 0000000..3c3019c
--- /dev/null
+++ b/test/t/bytes/testcase.proto
@@ -0,0 +1,11 @@
+
+option optimize_for = LITE_RUNTIME;
+
+package TestBytes;
+
+message Test {
+
+    required bytes s = 1;
+
+}
+
diff --git a/test/t/bytes/writer_test_cases.cpp b/test/t/bytes/writer_test_cases.cpp
new file mode 100644
index 0000000..140e852
--- /dev/null
+++ b/test/t/bytes/writer_test_cases.cpp
@@ -0,0 +1,54 @@
+
+#include <test.hpp>
+
+#include "test/t/bytes/testcase.pb.h"
+
+TEST_CASE("write bytes field and check with libprotobuf") {
+
+    std::string buffer;
+    protozero::pbf_writer pw(buffer);
+
+    TestBytes::Test msg;
+
+    SECTION("empty") {
+        pw.add_string(1, "");
+
+        msg.ParseFromString(buffer);
+
+        REQUIRE(msg.s() == "");
+    }
+
+    SECTION("one") {
+        pw.add_string(1, "x");
+
+        msg.ParseFromString(buffer);
+
+        REQUIRE(msg.s() == "x");
+    }
+
+    SECTION("string") {
+        pw.add_string(1, "foobar");
+
+        msg.ParseFromString(buffer);
+
+        REQUIRE(msg.s() == "foobar");
+    }
+
+    SECTION("binary") {
+        std::string data;
+        data.append(1, char(1));
+        data.append(1, char(2));
+        data.append(1, char(3));
+
+        pw.add_string(1, data);
+
+        msg.ParseFromString(buffer);
+
+        REQUIRE(msg.s().size() == 3);
+        REQUIRE(msg.s()[1] == char(2));
+        REQUIRE(msg.s()[2] == char(3));
+        REQUIRE(msg.s()[2] == char(3));
+    }
+
+}
+
diff --git a/test/t/complex/data-all.pbf b/test/t/complex/data-all.pbf
new file mode 100644
index 0000000..50cd466
Binary files /dev/null and b/test/t/complex/data-all.pbf differ
diff --git a/test/t/complex/data-minimal.pbf b/test/t/complex/data-minimal.pbf
new file mode 100644
index 0000000..cfea0f0
Binary files /dev/null and b/test/t/complex/data-minimal.pbf differ
diff --git a/test/t/complex/data-some.pbf b/test/t/complex/data-some.pbf
new file mode 100644
index 0000000..d509366
Binary files /dev/null and b/test/t/complex/data-some.pbf differ
diff --git a/test/t/complex/test_cases.cpp b/test/t/complex/test_cases.cpp
new file mode 100644
index 0000000..e74a11f
--- /dev/null
+++ b/test/t/complex/test_cases.cpp
@@ -0,0 +1,345 @@
+
+#include <test.hpp>
+
+TEST_CASE("read complex data") {
+
+    SECTION("minimal") {
+        std::string buffer = load_data("complex/data-minimal");
+
+        protozero::pbf_reader item(buffer);
+
+        while (item.next()) {
+            switch (item.tag()) {
+                case 1: {
+                    REQUIRE(item.get_fixed32() == 12345678L);
+                    break;
+                }
+                case 5: {
+                    protozero::pbf_reader subitem = item.get_message();
+                    REQUIRE(subitem.next());
+                    REQUIRE(subitem.get_string() == "foobar");
+                    REQUIRE(!subitem.next());
+                    break;
+                }
+                default: {
+                    REQUIRE(false); // should not be here
+                    break;
+                }
+            }
+        }
+    }
+
+    SECTION("some") {
+        std::string buffer = load_data("complex/data-some");
+
+        protozero::pbf_reader item(buffer);
+
+        uint32_t sum_of_u = 0;
+        while (item.next()) {
+            switch (item.tag()) {
+                case 1: {
+                    REQUIRE(item.get_fixed32() == 12345678L);
+                    break;
+                }
+                case 2: {
+                    REQUIRE(true);
+                    item.skip();
+                    break;
+                }
+                case 4: {
+                    sum_of_u += item.get_uint32();
+                    break;
+                }
+                case 5: {
+                    protozero::pbf_reader subitem = item.get_message();
+                    REQUIRE(subitem.next());
+                    REQUIRE(subitem.get_string() == "foobar");
+                    REQUIRE(!subitem.next());
+                    break;
+                }
+                default: {
+                    REQUIRE(false); // should not be here
+                    break;
+                }
+            }
+        }
+        REQUIRE(sum_of_u == 66);
+    }
+
+    SECTION("all") {
+        std::string buffer = load_data("complex/data-all");
+
+        protozero::pbf_reader item(buffer);
+
+        int number_of_u = 0;
+        while (item.next()) {
+            switch (item.tag()) {
+                case 1: {
+                    REQUIRE(item.get_fixed32() == 12345678L);
+                    break;
+                }
+                case 2: {
+                    REQUIRE(true);
+                    item.skip();
+                    break;
+                }
+                case 3: {
+                    REQUIRE(item.get_int64() == 555555555LL);
+                    break;
+                }
+                case 4: {
+                    item.skip();
+                    ++number_of_u;
+                    break;
+                }
+                case 5: {
+                    protozero::pbf_reader subitem = item.get_message();
+                    REQUIRE(subitem.next());
+                    REQUIRE(subitem.get_string() == "foobar");
+                    REQUIRE(!subitem.next());
+                    break;
+                }
+                case 7: {
+                    auto pi = item.get_packed_sint32();
+                    int32_t sum = 0;
+                    for (auto it = pi.first; it != pi.second; ++it) {
+                        sum += *it;
+                    }
+                    REQUIRE(sum == 5);
+                    break;
+                }
+                case 8: {
+                    REQUIRE(item.get_string() == "optionalstring");
+                    break;
+                }
+                default: {
+                    REQUIRE(false); // should not be here
+                    break;
+                }
+            }
+        }
+        REQUIRE(number_of_u == 5);
+    }
+
+    SECTION("skip everything") {
+        std::string buffer = load_data("complex/data-all");
+
+        protozero::pbf_reader item(buffer);
+
+        while (item.next()) {
+            switch (item.tag()) {
+                case 1:
+                case 2:
+                case 3:
+                case 4:
+                case 5:
+                case 7:
+                case 8:
+                    item.skip();
+                    break;
+                default: {
+                    REQUIRE(false); // should not be here
+                    break;
+                }
+            }
+        }
+    }
+
+}
+
+TEST_CASE("write complex data") {
+
+    SECTION("minimal") {
+        std::string buffer;
+        protozero::pbf_writer pw(buffer);
+        pw.add_fixed32(1, 12345678);
+
+        std::string submessage;
+        protozero::pbf_writer pws(submessage);
+        pws.add_string(1, "foobar");
+
+        pw.add_message(5, submessage);
+
+        protozero::pbf_reader item(buffer);
+
+        while (item.next()) {
+            switch (item.tag()) {
+                case 1: {
+                    REQUIRE(item.get_fixed32() == 12345678L);
+                    break;
+                }
+                case 5: {
+                    protozero::pbf_reader subitem = item.get_message();
+                    REQUIRE(subitem.next());
+                    REQUIRE(subitem.get_string() == "foobar");
+                    REQUIRE(!subitem.next());
+                    break;
+                }
+                default: {
+                    REQUIRE(false); // should not be here
+                    break;
+                }
+            }
+        }
+    }
+
+    SECTION("some") {
+        std::string buffer;
+        protozero::pbf_writer pw(buffer);
+        pw.add_fixed32(1, 12345678);
+
+        std::string submessage;
+        protozero::pbf_writer pws(submessage);
+        pws.add_string(1, "foobar");
+
+        pw.add_uint32(4, 22);
+        pw.add_uint32(4, 44);
+        pw.add_int64(2, -9876543);
+        pw.add_message(5, submessage);
+
+        protozero::pbf_reader item(buffer);
+
+        uint32_t sum_of_u = 0;
+        while (item.next()) {
+            switch (item.tag()) {
+                case 1: {
+                    REQUIRE(item.get_fixed32() == 12345678L);
+                    break;
+                }
+                case 2: {
+                    REQUIRE(true);
+                    item.skip();
+                    break;
+                }
+                case 4: {
+                    sum_of_u += item.get_uint32();
+                    break;
+                }
+                case 5: {
+                    protozero::pbf_reader subitem = item.get_message();
+                    REQUIRE(subitem.next());
+                    REQUIRE(subitem.get_string() == "foobar");
+                    REQUIRE(!subitem.next());
+                    break;
+                }
+                default: {
+                    REQUIRE(false); // should not be here
+                    break;
+                }
+            }
+        }
+        REQUIRE(sum_of_u == 66);
+    }
+
+    SECTION("all") {
+        std::string buffer;
+        protozero::pbf_writer pw(buffer);
+        pw.add_fixed32(1, 12345678);
+
+        std::string submessage;
+        protozero::pbf_writer pws(submessage);
+        pws.add_string(1, "foobar");
+        pw.add_message(5, submessage);
+
+        pw.add_uint32(4, 22);
+        pw.add_uint32(4, 44);
+        pw.add_int64(2, -9876543);
+        pw.add_uint32(4, 44);
+        pw.add_uint32(4, 66);
+        pw.add_uint32(4, 66);
+
+        int32_t d[] = { -17, 22 };
+        pw.add_packed_sint32(7, std::begin(d), std::end(d));
+
+        pw.add_int64(3, 555555555);
+
+        protozero::pbf_reader item(buffer);
+
+        int number_of_u = 0;
+        while (item.next()) {
+            switch (item.tag()) {
+                case 1: {
+                    REQUIRE(item.get_fixed32() == 12345678L);
+                    break;
+                }
+                case 2: {
+                    REQUIRE(true);
+                    item.skip();
+                    break;
+                }
+                case 3: {
+                    REQUIRE(item.get_int64() == 555555555LL);
+                    break;
+                }
+                case 4: {
+                    item.skip();
+                    ++number_of_u;
+                    break;
+                }
+                case 5: {
+                    protozero::pbf_reader subitem = item.get_message();
+                    REQUIRE(subitem.next());
+                    REQUIRE(subitem.get_string() == "foobar");
+                    REQUIRE(!subitem.next());
+                    break;
+                }
+                case 7: {
+                    auto pi = item.get_packed_sint32();
+                    int32_t sum = 0;
+                    for (auto it = pi.first; it != pi.second; ++it) {
+                        sum += *it;
+                    }
+                    REQUIRE(sum == 5);
+                    break;
+                }
+                case 8: {
+                    REQUIRE(item.get_string() == "optionalstring");
+                    break;
+                }
+                default: {
+                    REQUIRE(false); // should not be here
+                    break;
+                }
+            }
+        }
+        REQUIRE(number_of_u == 5);
+    }
+}
+
+static void check_message(const std::string& buffer) {
+    protozero::pbf_reader item(buffer);
+
+    while (item.next()) {
+        switch (item.tag()) {
+            case 1: {
+                REQUIRE(item.get_fixed32() == 42L);
+                break;
+            }
+            case 5: {
+                protozero::pbf_reader subitem = item.get_message();
+                REQUIRE(subitem.next());
+                REQUIRE(subitem.get_string() == "foobar");
+                REQUIRE(!subitem.next());
+                break;
+            }
+            default: {
+                REQUIRE(false); // should not be here
+                break;
+            }
+        }
+    }
+}
+
+TEST_CASE("write complex with subwriter") {
+    std::string buffer_test;
+    protozero::pbf_writer pbf_test(buffer_test);
+    pbf_test.add_fixed32(1, 42L);
+
+    SECTION("message in message") {
+        protozero::pbf_writer pbf_submessage(pbf_test, 5);
+        pbf_submessage.add_string(1, "foobar");
+    }
+
+    check_message(buffer_test);
+}
+
diff --git a/test/t/complex/testcase.cpp b/test/t/complex/testcase.cpp
new file mode 100644
index 0000000..7d53e99
--- /dev/null
+++ b/test/t/complex/testcase.cpp
@@ -0,0 +1,30 @@
+
+#include <testcase.hpp>
+#include "testcase.pb.h"
+
+int main(int c, char *argv[]) {
+    TestComplex::Test msg;
+
+    msg.set_f(12345678);
+    TestComplex::Sub* submsg = msg.mutable_submessage();
+    submsg->set_s("foobar");
+
+    write_to_file(msg, "data-minimal.pbf");
+
+    msg.add_u(22);
+    msg.add_u(44);
+    msg.set_i(-9876543);
+
+    write_to_file(msg, "data-some.pbf");
+
+    msg.set_s("optionalstring");
+    msg.add_u(44);
+    msg.add_u(66);
+    msg.add_u(66);
+    msg.add_d(-17);
+    msg.add_d(22);
+    msg.set_j(555555555);
+
+    write_to_file(msg, "data-all.pbf");
+}
+
diff --git a/test/t/complex/testcase.proto b/test/t/complex/testcase.proto
new file mode 100644
index 0000000..26ebf0e
--- /dev/null
+++ b/test/t/complex/testcase.proto
@@ -0,0 +1,19 @@
+
+option optimize_for = LITE_RUNTIME;
+
+package TestComplex;
+
+message Sub {
+    required string s = 1;
+}
+
+message Test {
+    required fixed32 f = 1;
+    optional int64 i = 2;
+    optional int64 j = 3;
+    required Sub submessage = 5;
+    optional string s = 8;
+    repeated uint32 u = 4;
+    repeated sint32 d = 7 [packed=true];
+}
+
diff --git a/test/t/double/data-neg.pbf b/test/t/double/data-neg.pbf
new file mode 100644
index 0000000..013e5e0
--- /dev/null
+++ b/test/t/double/data-neg.pbf
@@ -0,0 +1 @@
+	ףp=*��
\ No newline at end of file
diff --git a/test/t/double/data-pos.pbf b/test/t/double/data-pos.pbf
new file mode 100644
index 0000000..42cdbd1
--- /dev/null
+++ b/test/t/double/data-pos.pbf
@@ -0,0 +1 @@
+	�O��n�@
\ No newline at end of file
diff --git a/test/t/double/data-zero.pbf b/test/t/double/data-zero.pbf
new file mode 100644
index 0000000..5f47e98
Binary files /dev/null and b/test/t/double/data-zero.pbf differ
diff --git a/test/t/double/test_cases.cpp b/test/t/double/test_cases.cpp
new file mode 100644
index 0000000..b9e1eb3
--- /dev/null
+++ b/test/t/double/test_cases.cpp
@@ -0,0 +1,69 @@
+
+#include <test.hpp>
+
+TEST_CASE("read double field") {
+
+    SECTION("zero") {
+        std::string buffer = load_data("double/data-zero");
+
+        protozero::pbf_reader item(buffer);
+
+        REQUIRE(item.next());
+        REQUIRE(item.get_double() == Approx(0.0));
+        REQUIRE(!item.next());
+    }
+
+    SECTION("positive") {
+        std::string buffer = load_data("double/data-pos");
+
+        protozero::pbf_reader item(buffer);
+
+        REQUIRE(item.next());
+        REQUIRE(item.get_double() == Approx(4.893));
+        REQUIRE(!item.next());
+    }
+
+    SECTION("negative") {
+        std::string buffer = load_data("double/data-neg");
+
+        protozero::pbf_reader item(buffer);
+
+        REQUIRE(item.next());
+        REQUIRE(item.get_double() == Approx(-9232.33));
+        REQUIRE(!item.next());
+    }
+
+    SECTION("end_of_buffer") {
+        std::string buffer = load_data("double/data-neg");
+
+        for (size_t i=1; i < buffer.size(); ++i) {
+            protozero::pbf_reader item(buffer.data(), i);
+            REQUIRE(item.next());
+            REQUIRE_THROWS_AS(item.get_double(), protozero::end_of_buffer_exception);
+        }
+    }
+
+}
+
+TEST_CASE("write double field") {
+
+    std::string buffer;
+    protozero::pbf_writer pw(buffer);
+
+    SECTION("zero") {
+        pw.add_double(1, 0.0);
+        REQUIRE(buffer == load_data("double/data-zero"));
+    }
+
+    SECTION("positive") {
+        pw.add_double(1, 4.893);
+        REQUIRE(buffer == load_data("double/data-pos"));
+    }
+
+    SECTION("negative") {
+        pw.add_double(1, -9232.33);
+        REQUIRE(buffer == load_data("double/data-neg"));
+    }
+
+}
+
diff --git a/test/t/double/testcase.cpp b/test/t/double/testcase.cpp
new file mode 100644
index 0000000..7e44377
--- /dev/null
+++ b/test/t/double/testcase.cpp
@@ -0,0 +1,17 @@
+
+#include <testcase.hpp>
+#include "testcase.pb.h"
+
+int main(int c, char *argv[]) {
+    TestDouble::Test msg;
+
+    msg.set_x(0.0);
+    write_to_file(msg, "data-zero.pbf");
+
+    msg.set_x(4.893);
+    write_to_file(msg, "data-pos.pbf");
+
+    msg.set_x(-9232.33);
+    write_to_file(msg, "data-neg.pbf");
+}
+
diff --git a/test/t/double/testcase.proto b/test/t/double/testcase.proto
new file mode 100644
index 0000000..a4a4bf6
--- /dev/null
+++ b/test/t/double/testcase.proto
@@ -0,0 +1,11 @@
+
+option optimize_for = LITE_RUNTIME;
+
+package TestDouble;
+
+message Test {
+
+    required double x = 1;
+
+}
+
diff --git a/test/t/double/writer_test_cases.cpp b/test/t/double/writer_test_cases.cpp
new file mode 100644
index 0000000..bb29f52
--- /dev/null
+++ b/test/t/double/writer_test_cases.cpp
@@ -0,0 +1,38 @@
+
+#include <test.hpp>
+
+#include "test/t/double/testcase.pb.h"
+
+TEST_CASE("write double field and check with libprotobuf") {
+
+    std::string buffer;
+    protozero::pbf_writer pw(buffer);
+
+    TestDouble::Test msg;
+
+    SECTION("zero") {
+        pw.add_double(1, 0.0);
+
+        msg.ParseFromString(buffer);
+
+        REQUIRE(msg.x() == Approx(0.0));
+    }
+
+    SECTION("positive") {
+        pw.add_double(1, 4.893);
+
+        msg.ParseFromString(buffer);
+
+        REQUIRE(msg.x() == Approx(4.893));
+    }
+
+    SECTION("negative") {
+        pw.add_double(1, -9232.33);
+
+        msg.ParseFromString(buffer);
+
+        REQUIRE(msg.x() == Approx(-9232.33));
+    }
+
+}
+
diff --git a/test/t/enum/data-black.pbf b/test/t/enum/data-black.pbf
new file mode 100644
index 0000000..268720a
Binary files /dev/null and b/test/t/enum/data-black.pbf differ
diff --git a/test/t/enum/data-blue.pbf b/test/t/enum/data-blue.pbf
new file mode 100644
index 0000000..d65dd8f
--- /dev/null
+++ b/test/t/enum/data-blue.pbf
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/test/t/enum/test_cases.cpp b/test/t/enum/test_cases.cpp
new file mode 100644
index 0000000..61aca39
--- /dev/null
+++ b/test/t/enum/test_cases.cpp
@@ -0,0 +1,44 @@
+
+#include <test.hpp>
+
+TEST_CASE("read enum field") {
+
+    SECTION("zero") {
+        std::string buffer = load_data("enum/data-black");
+
+        protozero::pbf_reader item(buffer);
+
+        REQUIRE(item.next());
+        REQUIRE(item.get_enum() == 0L);
+        REQUIRE(!item.next());
+    }
+
+    SECTION("positive") {
+        std::string buffer = load_data("enum/data-blue");
+
+        protozero::pbf_reader item(buffer);
+
+        REQUIRE(item.next());
+        REQUIRE(item.get_enum() == 3L);
+        REQUIRE(!item.next());
+    }
+
+}
+
+TEST_CASE("write enum field") {
+
+    std::string buffer;
+    protozero::pbf_writer pw(buffer);
+
+    SECTION("zero") {
+        pw.add_enum(1, 0L);
+        REQUIRE(buffer == load_data("enum/data-black"));
+    }
+
+    SECTION("positive") {
+        pw.add_enum(1, 3L);
+        REQUIRE(buffer == load_data("enum/data-blue"));
+    }
+
+}
+
diff --git a/test/t/enum/testcase.cpp b/test/t/enum/testcase.cpp
new file mode 100644
index 0000000..79c95bd
--- /dev/null
+++ b/test/t/enum/testcase.cpp
@@ -0,0 +1,14 @@
+
+#include <testcase.hpp>
+#include "testcase.pb.h"
+
+int main(int c, char *argv[]) {
+    TestEnum::Test msg;
+
+    msg.set_color(TestEnum::BLACK);
+    write_to_file(msg, "data-black.pbf");
+
+    msg.set_color(TestEnum::BLUE);
+    write_to_file(msg, "data-blue.pbf");
+}
+
diff --git a/test/t/enum/testcase.proto b/test/t/enum/testcase.proto
new file mode 100644
index 0000000..0113b0d
--- /dev/null
+++ b/test/t/enum/testcase.proto
@@ -0,0 +1,18 @@
+
+option optimize_for = LITE_RUNTIME;
+
+package TestEnum;
+
+enum Color {
+    BLACK = 0;
+    RED   = 1;
+    GREEN = 2;
+    BLUE  = 3;
+}
+
+message Test {
+
+    required Color color = 1;
+
+}
+
diff --git a/test/t/enum/writer_test_cases.cpp b/test/t/enum/writer_test_cases.cpp
new file mode 100644
index 0000000..22803fb
--- /dev/null
+++ b/test/t/enum/writer_test_cases.cpp
@@ -0,0 +1,30 @@
+
+#include <test.hpp>
+
+#include "test/t/enum/testcase.pb.h"
+
+TEST_CASE("write enum field and check with libprotobuf") {
+
+    std::string buffer;
+    protozero::pbf_writer pw(buffer);
+
+    TestEnum::Test msg;
+
+    SECTION("zero") {
+        pw.add_enum(1, 0L);
+
+        msg.ParseFromString(buffer);
+
+        REQUIRE(msg.color() == TestEnum::Color::BLACK);
+    }
+
+    SECTION("positive") {
+        pw.add_enum(1, 3L);
+
+        msg.ParseFromString(buffer);
+
+        REQUIRE(msg.color() == TestEnum::Color::BLUE);
+    }
+
+}
+
diff --git a/test/t/exceptions/test_cases.cpp b/test/t/exceptions/test_cases.cpp
new file mode 100644
index 0000000..5e302c8
--- /dev/null
+++ b/test/t/exceptions/test_cases.cpp
@@ -0,0 +1,27 @@
+
+#include <test.hpp>
+
+TEST_CASE("exceptions messages") {
+
+    SECTION("pbf") {
+        protozero::exception e;
+        REQUIRE(std::string(e.what()) == std::string("pbf exception"));
+    }
+
+    SECTION("varint too long") {
+        protozero::varint_too_long_exception e;
+        REQUIRE(std::string(e.what()) == std::string("varint too long exception"));
+    }
+
+    SECTION("unknown pbf field type") {
+        protozero::unknown_pbf_wire_type_exception e;
+        REQUIRE(std::string(e.what()) == std::string("unknown pbf field type exception"));
+    }
+
+    SECTION("end of buffer") {
+        protozero::end_of_buffer_exception e;
+        REQUIRE(std::string(e.what()) == std::string("end of buffer exception"));
+    }
+
+}
+
diff --git a/test/t/fixed32/data-max-uint.pbf b/test/t/fixed32/data-max-uint.pbf
new file mode 100644
index 0000000..416d363
--- /dev/null
+++ b/test/t/fixed32/data-max-uint.pbf
@@ -0,0 +1 @@
+
����
\ No newline at end of file
diff --git a/test/t/fixed32/data-min-uint.pbf b/test/t/fixed32/data-min-uint.pbf
new file mode 100644
index 0000000..1cf2c40
Binary files /dev/null and b/test/t/fixed32/data-min-uint.pbf differ
diff --git a/test/t/fixed32/data-zero.pbf b/test/t/fixed32/data-zero.pbf
new file mode 100644
index 0000000..1cf2c40
Binary files /dev/null and b/test/t/fixed32/data-zero.pbf differ
diff --git a/test/t/fixed32/test_cases.cpp b/test/t/fixed32/test_cases.cpp
new file mode 100644
index 0000000..2e56264
--- /dev/null
+++ b/test/t/fixed32/test_cases.cpp
@@ -0,0 +1,102 @@
+
+#include <test.hpp>
+
+TEST_CASE("read fixed32 field") {
+
+    SECTION("zero") {
+        std::string buffer = load_data("fixed32/data-zero");
+
+        protozero::pbf_reader item(buffer);
+
+        REQUIRE(item.next());
+        REQUIRE(item.get_fixed32() == 0UL);
+        REQUIRE(!item.next());
+    }
+
+    SECTION("max-uint") {
+        std::string buffer = load_data("fixed32/data-max-uint");
+
+        protozero::pbf_reader item(buffer);
+
+        REQUIRE(item.next());
+        REQUIRE(item.get_fixed32() == std::numeric_limits<uint32_t>::max());
+        REQUIRE(!item.next());
+    }
+
+    SECTION("min-uint") {
+        std::string buffer = load_data("fixed32/data-min-uint");
+
+        protozero::pbf_reader item(buffer);
+
+        REQUIRE(item.next());
+        REQUIRE(item.get_fixed32() == std::numeric_limits<uint32_t>::min());
+        REQUIRE(!item.next());
+    }
+
+    SECTION("end_of_buffer") {
+        std::string buffer = load_data("fixed32/data-min-uint");
+
+        for (size_t i=1; i < buffer.size(); ++i) {
+            protozero::pbf_reader item(buffer.data(), i);
+            REQUIRE(item.next());
+            REQUIRE_THROWS_AS(item.get_fixed32(), protozero::end_of_buffer_exception);
+        }
+    }
+
+    SECTION("check assert on varint/string access") {
+        std::string buffer = load_data("fixed32/data-zero");
+
+        protozero::pbf_reader item(buffer);
+
+        REQUIRE(item.next());
+        REQUIRE_THROWS_AS(item.get_string(), assert_error);
+    }
+
+    SECTION("assert detecting tag==0") {
+        std::string buffer = load_data("fixed32/data-zero");
+
+        protozero::pbf_reader item(buffer);
+
+        REQUIRE_THROWS_AS(item.get_fixed32(), assert_error);
+        REQUIRE(item.next());
+        REQUIRE(item.get_fixed32() == 0UL);
+        REQUIRE_THROWS(item.get_fixed32());
+        REQUIRE(!item.next());
+    }
+
+    SECTION("skip") {
+        std::string buffer = load_data("fixed32/data-zero");
+
+        protozero::pbf_reader item(buffer);
+
+        REQUIRE_THROWS_AS(item.skip(), assert_error);
+        REQUIRE(item.next());
+        item.skip();
+        REQUIRE_THROWS(item.skip());
+        REQUIRE(!item.next());
+    }
+
+}
+
+TEST_CASE("write fixed32 field") {
+
+    std::string buffer;
+    protozero::pbf_writer pw(buffer);
+
+    SECTION("zero") {
+        pw.add_fixed32(1, 0);
+        REQUIRE(buffer == load_data("fixed32/data-zero"));
+    }
+
+    SECTION("max-uint") {
+        pw.add_fixed32(1, std::numeric_limits<uint32_t>::max());
+        REQUIRE(buffer == load_data("fixed32/data-max-uint"));
+    }
+
+    SECTION("min-uint") {
+        pw.add_fixed32(1, std::numeric_limits<uint32_t>::min());
+        REQUIRE(buffer == load_data("fixed32/data-min-uint"));
+    }
+
+}
+
diff --git a/test/t/fixed32/testcase.cpp b/test/t/fixed32/testcase.cpp
new file mode 100644
index 0000000..a4b58da
--- /dev/null
+++ b/test/t/fixed32/testcase.cpp
@@ -0,0 +1,17 @@
+
+#include <testcase.hpp>
+#include "testcase.pb.h"
+
+int main(int c, char *argv[]) {
+    TestFixed32::Test msg;
+
+    msg.set_i(0);
+    write_to_file(msg, "data-zero.pbf");
+
+    msg.set_i(std::numeric_limits<uint32_t>::max());
+    write_to_file(msg, "data-max-uint.pbf");
+
+    msg.set_i(std::numeric_limits<uint32_t>::min());
+    write_to_file(msg, "data-min-uint.pbf");
+}
+
diff --git a/test/t/fixed32/testcase.proto b/test/t/fixed32/testcase.proto
new file mode 100644
index 0000000..ab6334e
--- /dev/null
+++ b/test/t/fixed32/testcase.proto
@@ -0,0 +1,11 @@
+
+option optimize_for = LITE_RUNTIME;
+
+package TestFixed32;
+
+message Test {
+
+    required fixed32 i = 1;
+
+}
+
diff --git a/test/t/fixed32/writer_test_cases.cpp b/test/t/fixed32/writer_test_cases.cpp
new file mode 100644
index 0000000..55ff948
--- /dev/null
+++ b/test/t/fixed32/writer_test_cases.cpp
@@ -0,0 +1,38 @@
+
+#include <test.hpp>
+
+#include "test/t/fixed32/testcase.pb.h"
+
+TEST_CASE("write fixed32 field and check with libprotobuf") {
+
+    std::string buffer;
+    protozero::pbf_writer pw(buffer);
+
+    TestFixed32::Test msg;
+
+    SECTION("zero") {
+        pw.add_fixed32(1, 0);
+
+        msg.ParseFromString(buffer);
+
+        REQUIRE(msg.i() == 0);
+    }
+
+    SECTION("max-uint") {
+        pw.add_fixed32(1, std::numeric_limits<uint32_t>::max());
+
+        msg.ParseFromString(buffer);
+
+        REQUIRE(msg.i() == std::numeric_limits<uint32_t>::max());
+    }
+
+    SECTION("min-uint") {
+        pw.add_fixed32(1, std::numeric_limits<uint32_t>::min());
+
+        msg.ParseFromString(buffer);
+
+        REQUIRE(msg.i() == std::numeric_limits<uint32_t>::min());
+    }
+
+}
+
diff --git a/test/t/fixed64/data-max-uint.pbf b/test/t/fixed64/data-max-uint.pbf
new file mode 100644
index 0000000..cb810a5
--- /dev/null
+++ b/test/t/fixed64/data-max-uint.pbf
@@ -0,0 +1 @@
+	��������
\ No newline at end of file
diff --git a/test/t/fixed64/data-min-uint.pbf b/test/t/fixed64/data-min-uint.pbf
new file mode 100644
index 0000000..5f47e98
Binary files /dev/null and b/test/t/fixed64/data-min-uint.pbf differ
diff --git a/test/t/fixed64/data-zero.pbf b/test/t/fixed64/data-zero.pbf
new file mode 100644
index 0000000..5f47e98
Binary files /dev/null and b/test/t/fixed64/data-zero.pbf differ
diff --git a/test/t/fixed64/test_cases.cpp b/test/t/fixed64/test_cases.cpp
new file mode 100644
index 0000000..9fbb1ef
--- /dev/null
+++ b/test/t/fixed64/test_cases.cpp
@@ -0,0 +1,69 @@
+
+#include <test.hpp>
+
+TEST_CASE("read fixed64 field") {
+
+    SECTION("zero") {
+        std::string buffer = load_data("fixed64/data-zero");
+
+        protozero::pbf_reader item(buffer);
+
+        REQUIRE(item.next());
+        REQUIRE(item.get_fixed64() == 0ULL);
+        REQUIRE(!item.next());
+    }
+
+    SECTION("max-uint") {
+        std::string buffer = load_data("fixed64/data-max-uint");
+
+        protozero::pbf_reader item(buffer);
+
+        REQUIRE(item.next());
+        REQUIRE(item.get_fixed64() == std::numeric_limits<uint64_t>::max());
+        REQUIRE(!item.next());
+    }
+
+    SECTION("min-uint") {
+        std::string buffer = load_data("fixed64/data-min-uint");
+
+        protozero::pbf_reader item(buffer);
+
+        REQUIRE(item.next());
+        REQUIRE(item.get_fixed64() == std::numeric_limits<uint64_t>::min());
+        REQUIRE(!item.next());
+    }
+
+    SECTION("end_of_buffer") {
+        std::string buffer = load_data("fixed64/data-min-uint");
+
+        for (size_t i=1; i < buffer.size(); ++i) {
+            protozero::pbf_reader item(buffer.data(), i);
+            REQUIRE(item.next());
+            REQUIRE_THROWS_AS(item.get_fixed64(), protozero::end_of_buffer_exception);
+        }
+    }
+
+}
+
+TEST_CASE("write fixed64 field") {
+
+    std::string buffer;
+    protozero::pbf_writer pw(buffer);
+
+    SECTION("zero") {
+        pw.add_fixed64(1, 0ULL);
+        REQUIRE(buffer == load_data("fixed64/data-zero"));
+    }
+
+    SECTION("max-uint") {
+        pw.add_fixed64(1, std::numeric_limits<uint64_t>::max());
+        REQUIRE(buffer == load_data("fixed64/data-max-uint"));
+    }
+
+    SECTION("min-uint") {
+        pw.add_fixed64(1, std::numeric_limits<uint64_t>::min());
+        REQUIRE(buffer == load_data("fixed64/data-min-uint"));
+    }
+
+}
+
diff --git a/test/t/fixed64/testcase.cpp b/test/t/fixed64/testcase.cpp
new file mode 100644
index 0000000..09a4d79
--- /dev/null
+++ b/test/t/fixed64/testcase.cpp
@@ -0,0 +1,17 @@
+
+#include <testcase.hpp>
+#include "testcase.pb.h"
+
+int main(int c, char *argv[]) {
+    TestFixed64::Test msg;
+
+    msg.set_i(0);
+    write_to_file(msg, "data-zero.pbf");
+
+    msg.set_i(std::numeric_limits<uint64_t>::max());
+    write_to_file(msg, "data-max-uint.pbf");
+
+    msg.set_i(std::numeric_limits<uint64_t>::min());
+    write_to_file(msg, "data-min-uint.pbf");
+}
+
diff --git a/test/t/fixed64/testcase.proto b/test/t/fixed64/testcase.proto
new file mode 100644
index 0000000..6e3cce2
--- /dev/null
+++ b/test/t/fixed64/testcase.proto
@@ -0,0 +1,11 @@
+
+option optimize_for = LITE_RUNTIME;
+
+package TestFixed64;
+
+message Test {
+
+    required fixed64 i = 1;
+
+}
+
diff --git a/test/t/float/data-neg.pbf b/test/t/float/data-neg.pbf
new file mode 100644
index 0000000..4229a69
--- /dev/null
+++ b/test/t/float/data-neg.pbf
@@ -0,0 +1 @@
+
H�ڿ
\ No newline at end of file
diff --git a/test/t/float/data-pos.pbf b/test/t/float/data-pos.pbf
new file mode 100644
index 0000000..c94c6df
--- /dev/null
+++ b/test/t/float/data-pos.pbf
@@ -0,0 +1 @@
+
H�@
\ No newline at end of file
diff --git a/test/t/float/data-zero.pbf b/test/t/float/data-zero.pbf
new file mode 100644
index 0000000..1cf2c40
Binary files /dev/null and b/test/t/float/data-zero.pbf differ
diff --git a/test/t/float/test_cases.cpp b/test/t/float/test_cases.cpp
new file mode 100644
index 0000000..48721c1
--- /dev/null
+++ b/test/t/float/test_cases.cpp
@@ -0,0 +1,69 @@
+
+#include <test.hpp>
+
+TEST_CASE("read float field") {
+
+    SECTION("zero") {
+        std::string buffer = load_data("float/data-zero");
+
+        protozero::pbf_reader item(buffer);
+
+        REQUIRE(item.next());
+        REQUIRE(item.get_float() == Approx(0.0f));
+        REQUIRE(!item.next());
+    }
+
+    SECTION("positive") {
+        std::string buffer = load_data("float/data-pos");
+
+        protozero::pbf_reader item(buffer);
+
+        REQUIRE(item.next());
+        REQUIRE(item.get_float() == Approx(5.34f));
+        REQUIRE(!item.next());
+    }
+
+    SECTION("negative") {
+        std::string buffer = load_data("float/data-neg");
+
+        protozero::pbf_reader item(buffer);
+
+        REQUIRE(item.next());
+        REQUIRE(item.get_float() == Approx(-1.71f));
+        REQUIRE(!item.next());
+    }
+
+    SECTION("end_of_buffer") {
+        std::string buffer = load_data("float/data-neg");
+
+        for (size_t i=1; i < buffer.size(); ++i) {
+            protozero::pbf_reader item(buffer.data(), i);
+            REQUIRE(item.next());
+            REQUIRE_THROWS_AS(item.get_float(), protozero::end_of_buffer_exception);
+        }
+    }
+
+}
+
+TEST_CASE("write float field") {
+
+    std::string buffer;
+    protozero::pbf_writer pw(buffer);
+
+    SECTION("zero") {
+        pw.add_float(1, 0.0f);
+        REQUIRE(buffer == load_data("float/data-zero"));
+    }
+
+    SECTION("positive") {
+        pw.add_float(1, 5.34f);
+        REQUIRE(buffer == load_data("float/data-pos"));
+    }
+
+    SECTION("negative") {
+        pw.add_float(1, -1.71f);
+        REQUIRE(buffer == load_data("float/data-neg"));
+    }
+
+}
+
diff --git a/test/t/float/testcase.cpp b/test/t/float/testcase.cpp
new file mode 100644
index 0000000..c7a743b
--- /dev/null
+++ b/test/t/float/testcase.cpp
@@ -0,0 +1,17 @@
+
+#include <testcase.hpp>
+#include "testcase.pb.h"
+
+int main(int c, char *argv[]) {
+    TestFloat::Test msg;
+
+    msg.set_x(0.0);
+    write_to_file(msg, "data-zero.pbf");
+
+    msg.set_x(5.34);
+    write_to_file(msg, "data-pos.pbf");
+
+    msg.set_x(-1.71);
+    write_to_file(msg, "data-neg.pbf");
+}
+
diff --git a/test/t/float/testcase.proto b/test/t/float/testcase.proto
new file mode 100644
index 0000000..db5d8b1
--- /dev/null
+++ b/test/t/float/testcase.proto
@@ -0,0 +1,11 @@
+
+option optimize_for = LITE_RUNTIME;
+
+package TestFloat;
+
+message Test {
+
+    required float x = 1;
+
+}
+
diff --git a/test/t/int32/data-max.pbf b/test/t/int32/data-max.pbf
new file mode 100644
index 0000000..f7d9ded
--- /dev/null
+++ b/test/t/int32/data-max.pbf
@@ -0,0 +1 @@
+����
\ No newline at end of file
diff --git a/test/t/int32/data-min.pbf b/test/t/int32/data-min.pbf
new file mode 100644
index 0000000..df5f9bd
--- /dev/null
+++ b/test/t/int32/data-min.pbf
@@ -0,0 +1 @@
+���������
\ No newline at end of file
diff --git a/test/t/int32/data-neg.pbf b/test/t/int32/data-neg.pbf
new file mode 100644
index 0000000..1c2f041
--- /dev/null
+++ b/test/t/int32/data-neg.pbf
@@ -0,0 +1 @@
+���������
\ No newline at end of file
diff --git a/test/t/int32/data-pos.pbf b/test/t/int32/data-pos.pbf
new file mode 100644
index 0000000..e19a122
--- /dev/null
+++ b/test/t/int32/data-pos.pbf
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/test/t/int32/data-zero.pbf b/test/t/int32/data-zero.pbf
new file mode 100644
index 0000000..268720a
Binary files /dev/null and b/test/t/int32/data-zero.pbf differ
diff --git a/test/t/int32/test_cases.cpp b/test/t/int32/test_cases.cpp
new file mode 100644
index 0000000..7af603d
--- /dev/null
+++ b/test/t/int32/test_cases.cpp
@@ -0,0 +1,119 @@
+
+#include <test.hpp>
+
+TEST_CASE("read int32 field") {
+
+    SECTION("zero") {
+        std::string buffer = load_data("int32/data-zero");
+
+        protozero::pbf_reader item(buffer);
+
+        REQUIRE(item.next());
+        REQUIRE(item.get_int32() == 0L);
+        REQUIRE(!item.next());
+    }
+
+    SECTION("positive") {
+        std::string buffer = load_data("int32/data-pos");
+
+        protozero::pbf_reader item(buffer);
+
+        REQUIRE(item.next());
+        REQUIRE(item.get_int32() == 1L);
+        REQUIRE(!item.next());
+    }
+
+    SECTION("negative") {
+        std::string buffer = load_data("int32/data-neg");
+
+        protozero::pbf_reader item(buffer);
+
+        REQUIRE(item.next());
+        REQUIRE(item.get_int32() == -1L);
+        REQUIRE(!item.next());
+    }
+
+    SECTION("max") {
+        std::string buffer = load_data("int32/data-max");
+
+        protozero::pbf_reader item(buffer);
+
+        REQUIRE(item.next());
+        REQUIRE(item.get_int32() == std::numeric_limits<int32_t>::max());
+        REQUIRE(!item.next());
+    }
+
+    SECTION("min64") {
+        std::string buffer = load_data("int32/data-min");
+
+        protozero::pbf_reader item(buffer);
+
+        REQUIRE(item.next());
+        REQUIRE(static_cast<int32_t>(item.get_int64()) == std::numeric_limits<int32_t>::min());
+        REQUIRE(!item.next());
+    }
+
+    SECTION("min") {
+        std::string buffer = load_data("int32/data-min");
+
+        protozero::pbf_reader item(buffer);
+
+        REQUIRE(item.next());
+        REQUIRE(item.get_int32() == std::numeric_limits<int32_t>::min());
+        REQUIRE(!item.next());
+    }
+
+    SECTION("end_of_buffer") {
+        std::string buffer = load_data("int32/data-min");
+
+        for (size_t i=1; i < buffer.size(); ++i) {
+            protozero::pbf_reader item(buffer.data(), i);
+            REQUIRE(item.next());
+            REQUIRE_THROWS_AS(item.get_int32(), protozero::end_of_buffer_exception);
+        }
+    }
+
+    SECTION("check assert on string/fixed int access") {
+        std::string buffer = load_data("int32/data-zero");
+
+        protozero::pbf_reader item(buffer);
+
+        REQUIRE(item.next());
+        REQUIRE_THROWS_AS(item.get_fixed32(), assert_error);
+        REQUIRE_THROWS_AS(item.get_string(), assert_error);
+    }
+
+}
+
+TEST_CASE("write int32 field") {
+
+    std::string buffer;
+    protozero::pbf_writer pw(buffer);
+
+    SECTION("zero") {
+        pw.add_int32(1, 0L);
+        REQUIRE(buffer == load_data("int32/data-zero"));
+    }
+
+    SECTION("positive") {
+        pw.add_int32(1, 1L);
+        REQUIRE(buffer == load_data("int32/data-pos"));
+    }
+
+    SECTION("negative") {
+        pw.add_int32(1, -1L);
+        REQUIRE(buffer == load_data("int32/data-neg"));
+    }
+
+    SECTION("max") {
+        pw.add_int32(1, std::numeric_limits<int32_t>::max());
+        REQUIRE(buffer == load_data("int32/data-max"));
+    }
+
+    SECTION("min") {
+        pw.add_int32(1, std::numeric_limits<int32_t>::min());
+        REQUIRE(buffer == load_data("int32/data-min"));
+    }
+
+}
+
diff --git a/test/t/int32/testcase.cpp b/test/t/int32/testcase.cpp
new file mode 100644
index 0000000..9347b90
--- /dev/null
+++ b/test/t/int32/testcase.cpp
@@ -0,0 +1,23 @@
+
+#include <testcase.hpp>
+#include "testcase.pb.h"
+
+int main(int c, char *argv[]) {
+    TestInt32::Test msg;
+
+    msg.set_i(0);
+    write_to_file(msg, "data-zero.pbf");
+
+    msg.set_i(1);
+    write_to_file(msg, "data-pos.pbf");
+
+    msg.set_i(-1);
+    write_to_file(msg, "data-neg.pbf");
+
+    msg.set_i(std::numeric_limits<int32_t>::max());
+    write_to_file(msg, "data-max.pbf");
+
+    msg.set_i(std::numeric_limits<int32_t>::min());
+    write_to_file(msg, "data-min.pbf");
+}
+
diff --git a/test/t/int32/testcase.proto b/test/t/int32/testcase.proto
new file mode 100644
index 0000000..c80a4bd
--- /dev/null
+++ b/test/t/int32/testcase.proto
@@ -0,0 +1,11 @@
+
+option optimize_for = LITE_RUNTIME;
+
+package TestInt32;
+
+message Test {
+
+    required int32 i = 1;
+
+}
+
diff --git a/test/t/int32/writer_test_cases.cpp b/test/t/int32/writer_test_cases.cpp
new file mode 100644
index 0000000..dc3f848
--- /dev/null
+++ b/test/t/int32/writer_test_cases.cpp
@@ -0,0 +1,54 @@
+
+#include <test.hpp>
+
+#include "test/t/int32/testcase.pb.h"
+
+TEST_CASE("write int32 field and check with libprotobuf") {
+
+    std::string buffer;
+    protozero::pbf_writer pw(buffer);
+
+    TestInt32::Test msg;
+
+    SECTION("zero") {
+        pw.add_int32(1, 0L);
+
+        msg.ParseFromString(buffer);
+
+        REQUIRE(msg.i() == 0L);
+    }
+
+    SECTION("positive") {
+        pw.add_int32(1, 1L);
+
+        msg.ParseFromString(buffer);
+
+        REQUIRE(msg.i() == 1L);
+    }
+
+    SECTION("negative") {
+        pw.add_int32(1, -1L);
+
+        msg.ParseFromString(buffer);
+
+        REQUIRE(msg.i() == -1L);
+    }
+
+    SECTION("max") {
+        pw.add_int32(1, std::numeric_limits<int32_t>::max());
+
+        msg.ParseFromString(buffer);
+
+        REQUIRE(msg.i() == std::numeric_limits<int32_t>::max());
+    }
+
+    SECTION("min") {
+        pw.add_int32(1, std::numeric_limits<int32_t>::min());
+
+        msg.ParseFromString(buffer);
+
+        REQUIRE(msg.i() == std::numeric_limits<int32_t>::min());
+    }
+
+}
+
diff --git a/test/t/int64/data-max.pbf b/test/t/int64/data-max.pbf
new file mode 100644
index 0000000..e6fd4b5
--- /dev/null
+++ b/test/t/int64/data-max.pbf
@@ -0,0 +1 @@
+��������
\ No newline at end of file
diff --git a/test/t/int64/data-min.pbf b/test/t/int64/data-min.pbf
new file mode 100644
index 0000000..ee99dcc
--- /dev/null
+++ b/test/t/int64/data-min.pbf
@@ -0,0 +1 @@
+���������
\ No newline at end of file
diff --git a/test/t/int64/data-neg.pbf b/test/t/int64/data-neg.pbf
new file mode 100644
index 0000000..1c2f041
--- /dev/null
+++ b/test/t/int64/data-neg.pbf
@@ -0,0 +1 @@
+���������
\ No newline at end of file
diff --git a/test/t/int64/data-overflow.pbf b/test/t/int64/data-overflow.pbf
new file mode 100644
index 0000000..bfb65e2
--- /dev/null
+++ b/test/t/int64/data-overflow.pbf
@@ -0,0 +1 @@
+����������
\ No newline at end of file
diff --git a/test/t/int64/data-pos.pbf b/test/t/int64/data-pos.pbf
new file mode 100644
index 0000000..e19a122
--- /dev/null
+++ b/test/t/int64/data-pos.pbf
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/test/t/int64/data-zero.pbf b/test/t/int64/data-zero.pbf
new file mode 100644
index 0000000..268720a
Binary files /dev/null and b/test/t/int64/data-zero.pbf differ
diff --git a/test/t/int64/test_cases.cpp b/test/t/int64/test_cases.cpp
new file mode 100644
index 0000000..cb815ac
--- /dev/null
+++ b/test/t/int64/test_cases.cpp
@@ -0,0 +1,108 @@
+
+#include <test.hpp>
+
+TEST_CASE("read int64 field") {
+
+    SECTION("zero") {
+        std::string buffer = load_data("int64/data-zero");
+
+        protozero::pbf_reader item(buffer);
+
+        REQUIRE(item.next());
+        REQUIRE(item.get_int64() == 0LL);
+        REQUIRE(!item.next());
+    }
+
+    SECTION("positive") {
+        std::string buffer = load_data("int64/data-pos");
+
+        protozero::pbf_reader item(buffer);
+
+        REQUIRE(item.next());
+        REQUIRE(item.get_int64() == 1LL);
+        REQUIRE(!item.next());
+    }
+
+    SECTION("negative") {
+        std::string buffer = load_data("int64/data-neg");
+
+        protozero::pbf_reader item(buffer);
+
+        REQUIRE(item.next());
+        REQUIRE(item.get_int64() == -1LL);
+        REQUIRE(!item.next());
+    }
+
+    SECTION("max") {
+        std::string buffer = load_data("int64/data-max");
+
+        protozero::pbf_reader item(buffer);
+
+        REQUIRE(item.next());
+        REQUIRE(item.get_int64() == std::numeric_limits<int64_t>::max());
+        REQUIRE(!item.next());
+    }
+
+    SECTION("min") {
+        std::string buffer = load_data("int64/data-min");
+
+        protozero::pbf_reader item(buffer);
+
+        REQUIRE(item.next());
+        REQUIRE(item.get_int64() == std::numeric_limits<int64_t>::min());
+        REQUIRE(!item.next());
+    }
+
+    SECTION("end_of_buffer") {
+        std::string buffer = load_data("int64/data-min");
+
+        for (size_t i=1; i < buffer.size(); ++i) {
+            protozero::pbf_reader item(buffer.data(), i);
+            REQUIRE(item.next());
+            REQUIRE_THROWS_AS(item.get_int64(), protozero::end_of_buffer_exception);
+        }
+    }
+
+    SECTION("varint overflow") {
+        std::string buffer = load_data("int64/data-overflow");
+
+        protozero::pbf_reader item(buffer);
+
+        REQUIRE(item.next());
+        REQUIRE_THROWS_AS(item.get_int64(), protozero::varint_too_long_exception);
+    }
+
+}
+
+TEST_CASE("write int64 field") {
+
+    std::string buffer;
+    protozero::pbf_writer pw(buffer);
+
+    SECTION("zero") {
+        pw.add_int64(1, 0LL);
+        REQUIRE(buffer == load_data("int64/data-zero"));
+    }
+
+    SECTION("positive") {
+        pw.add_int64(1, 1LL);
+        REQUIRE(buffer == load_data("int64/data-pos"));
+    }
+
+    SECTION("negative") {
+        pw.add_int64(1, -1LL);
+        REQUIRE(buffer == load_data("int64/data-neg"));
+    }
+
+    SECTION("max") {
+        pw.add_int64(1, std::numeric_limits<int64_t>::max());
+        REQUIRE(buffer == load_data("int64/data-max"));
+    }
+
+    SECTION("min") {
+        pw.add_int64(1, std::numeric_limits<int64_t>::min());
+        REQUIRE(buffer == load_data("int64/data-min"));
+    }
+
+}
+
diff --git a/test/t/int64/testcase.cpp b/test/t/int64/testcase.cpp
new file mode 100644
index 0000000..c8add03
--- /dev/null
+++ b/test/t/int64/testcase.cpp
@@ -0,0 +1,33 @@
+
+#include <testcase.hpp>
+#include "testcase.pb.h"
+
+int main(int c, char *argv[]) {
+    TestInt64::Test msg;
+
+    msg.set_i(0);
+    write_to_file(msg, "data-zero.pbf");
+
+    msg.set_i(1);
+    write_to_file(msg, "data-pos.pbf");
+
+    msg.set_i(-1);
+    write_to_file(msg, "data-neg.pbf");
+
+    msg.set_i(std::numeric_limits<int64_t>::max());
+    write_to_file(msg, "data-max.pbf");
+
+    msg.set_i(std::numeric_limits<int64_t>::min());
+    std::string data = write_to_file(msg, "data-min.pbf");
+
+    // this is a bit of binary trickery:
+    // we add a 0x80 byte one before the end of the data
+    // this will lead to a varint thats 11 bytes long, which is not allowed
+    data.insert(10, 1, 0x80);
+    {
+        std::ofstream d("data-overflow.pbf");
+        assert(d.is_open());
+        d << data;
+    }
+}
+
diff --git a/test/t/int64/testcase.proto b/test/t/int64/testcase.proto
new file mode 100644
index 0000000..61a2975
--- /dev/null
+++ b/test/t/int64/testcase.proto
@@ -0,0 +1,11 @@
+
+option optimize_for = LITE_RUNTIME;
+
+package TestInt64;
+
+message Test {
+
+    required int64 i = 1;
+
+}
+
diff --git a/test/t/message/data-message.pbf b/test/t/message/data-message.pbf
new file mode 100644
index 0000000..7a002fb
--- /dev/null
+++ b/test/t/message/data-message.pbf
@@ -0,0 +1,3 @@
+
+
+foobar
\ No newline at end of file
diff --git a/test/t/message/data-opt-element.pbf b/test/t/message/data-opt-element.pbf
new file mode 100644
index 0000000..d60b0ab
--- /dev/null
+++ b/test/t/message/data-opt-element.pbf
@@ -0,0 +1,2 @@
+
+optional
\ No newline at end of file
diff --git a/test/t/message/data-opt-empty.pbf b/test/t/message/data-opt-empty.pbf
new file mode 100644
index 0000000..e69de29
diff --git a/test/t/message/test_cases.cpp b/test/t/message/test_cases.cpp
new file mode 100644
index 0000000..ef92064
--- /dev/null
+++ b/test/t/message/test_cases.cpp
@@ -0,0 +1,88 @@
+
+#include <test.hpp>
+
+TEST_CASE("read message field") {
+
+    SECTION("string") {
+        std::string buffer = load_data("message/data-message");
+
+        protozero::pbf_reader item(buffer);
+
+        REQUIRE(item.next());
+        protozero::pbf_reader subitem { item.get_message() };
+        REQUIRE(!item.next());
+
+        REQUIRE(subitem.next());
+        REQUIRE(subitem.get_string() == "foobar");
+        REQUIRE(!subitem.next());
+    }
+
+    SECTION("end_of_buffer") {
+        std::string buffer = load_data("message/data-message");
+
+        for (size_t i=1; i < buffer.size(); ++i) {
+            protozero::pbf_reader item(buffer.data(), i);
+            REQUIRE(item.next());
+            REQUIRE_THROWS_AS(item.get_string(), protozero::end_of_buffer_exception);
+        }
+    }
+
+    SECTION("optional contents of message - empty") {
+        std::string buffer = load_data("message/data-opt-empty");
+
+        protozero::pbf_reader item(buffer);
+
+        REQUIRE(!item.next());
+    }
+
+    SECTION("string") {
+        std::string buffer = load_data("message/data-opt-element");
+
+        protozero::pbf_reader item(buffer);
+
+        REQUIRE(item.next());
+        REQUIRE(item.get_string() == "optional");
+        REQUIRE(!item.next());
+    }
+
+}
+
+TEST_CASE("write message field") {
+
+    std::string buffer_test;
+    protozero::pbf_writer pbf_test(buffer_test);
+
+    SECTION("string") {
+        std::string buffer_submessage;
+        protozero::pbf_writer pbf_submessage(buffer_submessage);
+        pbf_submessage.add_string(1, "foobar");
+
+        pbf_test.add_message(1, buffer_submessage);
+    }
+
+    SECTION("string with subwriter") {
+        protozero::pbf_writer pbf_submessage(pbf_test, 1);
+        pbf_submessage.add_string(1, "foobar");
+    }
+
+    REQUIRE(buffer_test == load_data("message/data-message"));
+
+}
+
+TEST_CASE("write optional message field") {
+
+    std::string buffer_opt;
+    protozero::pbf_writer pbf_opt(buffer_opt);
+
+    SECTION("add nothing") {
+        REQUIRE(buffer_opt == load_data("message/data-opt-empty"));
+    }
+
+    SECTION("add string") {
+        pbf_opt.add_string(1, "optional");
+
+        REQUIRE(buffer_opt == load_data("message/data-opt-element"));
+    }
+
+}
+
diff --git a/test/t/message/testcase.cpp b/test/t/message/testcase.cpp
new file mode 100644
index 0000000..f25286f
--- /dev/null
+++ b/test/t/message/testcase.cpp
@@ -0,0 +1,19 @@
+
+#include <testcase.hpp>
+#include "testcase.pb.h"
+
+int main(int c, char *argv[]) {
+    TestMessage::Test msg;
+
+    TestMessage::Sub* submsg = msg.mutable_submessage();
+    submsg->set_s("foobar");
+
+    write_to_file(msg, "data-message.pbf");
+
+    TestMessage::Opt opt_msg;
+    write_to_file(opt_msg, "data-opt-empty.pbf");
+
+    opt_msg.set_s("optional");
+    write_to_file(opt_msg, "data-opt-element.pbf");
+}
+
diff --git a/test/t/message/testcase.proto b/test/t/message/testcase.proto
new file mode 100644
index 0000000..eee38ef
--- /dev/null
+++ b/test/t/message/testcase.proto
@@ -0,0 +1,17 @@
+
+option optimize_for = LITE_RUNTIME;
+
+package TestMessage;
+
+message Sub {
+    required string s = 1;
+}
+
+message Test {
+    required Sub submessage = 1;
+}
+
+message Opt {
+    optional string s = 1;
+}
+
diff --git a/test/t/message/writer_test_cases.cpp b/test/t/message/writer_test_cases.cpp
new file mode 100644
index 0000000..2f70a86
--- /dev/null
+++ b/test/t/message/writer_test_cases.cpp
@@ -0,0 +1,29 @@
+
+#include <test.hpp>
+
+#include "test/t/message/testcase.pb.h"
+
+TEST_CASE("write message field and check with libprotobuf") {
+
+    std::string buffer_test;
+    protozero::pbf_writer pbf_test(buffer_test);
+
+    SECTION("string") {
+        std::string buffer_submessage;
+        protozero::pbf_writer pbf_submessage(buffer_submessage);
+        pbf_submessage.add_string(1, "foobar");
+
+        pbf_test.add_message(1, buffer_submessage);
+    }
+
+    SECTION("string with subwriter") {
+        protozero::pbf_writer pbf_submessage(pbf_test, 1);
+        pbf_submessage.add_string(1, "foobar");
+    }
+
+    TestMessage::Test msg;
+    msg.ParseFromString(buffer_test);
+    REQUIRE(msg.submessage().s() == "foobar");
+
+}
+
diff --git a/test/t/nested/data-message.pbf b/test/t/nested/data-message.pbf
new file mode 100644
index 0000000..6856641
--- /dev/null
+++ b/test/t/nested/data-message.pbf
@@ -0,0 +1,5 @@
+
+
+
+
+foobarcXM
\ No newline at end of file
diff --git a/test/t/nested/data-no-message.pbf b/test/t/nested/data-no-message.pbf
new file mode 100644
index 0000000..82be040
Binary files /dev/null and b/test/t/nested/data-no-message.pbf differ
diff --git a/test/t/nested/test_cases.cpp b/test/t/nested/test_cases.cpp
new file mode 100644
index 0000000..bf74bc9
--- /dev/null
+++ b/test/t/nested/test_cases.cpp
@@ -0,0 +1,163 @@
+
+#include <test.hpp>
+
+inline void check_subsub(protozero::pbf_reader message) {
+    while (message.next()) {
+        switch (message.tag()) {
+            case 1: {
+                REQUIRE("foobar" == message.get_string());
+                break;
+            }
+            case 2: {
+                REQUIRE(99 == message.get_int32());
+                break;
+            }
+            default: {
+                REQUIRE(false); // should never be here
+                break;
+            }
+        }
+    }
+}
+
+inline void check_sub(protozero::pbf_reader message) {
+    while (message.next()) {
+        switch (message.tag()) {
+            case 1: {
+                check_subsub(message.get_message());
+                break;
+            }
+            case 2: {
+                REQUIRE(88 == message.get_int32());
+                break;
+            }
+            default: {
+                REQUIRE(false); // should never be here
+                break;
+            }
+        }
+    }
+}
+
+inline void check(protozero::pbf_reader message) {
+    while (message.next()) {
+        switch (message.tag()) {
+            case 1: {
+                check_sub(message.get_message());
+                break;
+            }
+            case 2: {
+                REQUIRE(77 == message.get_int32());
+                break;
+            }
+            default: {
+                REQUIRE(false); // should never be here
+                break;
+            }
+        }
+    }
+}
+
+inline void check_empty(protozero::pbf_reader message) {
+    while (message.next()) {
+        switch (message.tag()) {
+            case 1: {
+                REQUIRE(!message.get_message().next());
+                break;
+            }
+            case 2: {
+                REQUIRE(77 == message.get_int32());
+                break;
+            }
+            default: {
+                REQUIRE(false); // should never be here
+                break;
+            }
+        }
+    }
+}
+
+TEST_CASE("read nested message fields") {
+
+    SECTION("string") {
+        std::string buffer = load_data("nested/data-message");
+
+        protozero::pbf_reader message(buffer);
+        check(message);
+    }
+
+    SECTION("no submessage") {
+        std::string buffer = load_data("nested/data-no-message");
+
+        protozero::pbf_reader message(buffer);
+        check_empty(message);
+    }
+
+}
+
+TEST_CASE("write nested message fields") {
+
+    std::string buffer_test;
+    protozero::pbf_writer pbf_test(buffer_test);
+
+    SECTION("string") {
+        std::string buffer_subsub;
+        protozero::pbf_writer pbf_subsub(buffer_subsub);
+        pbf_subsub.add_string(1, "foobar");
+        pbf_subsub.add_int32(2, 99);
+
+        std::string buffer_sub;
+        protozero::pbf_writer pbf_sub(buffer_sub);
+        pbf_sub.add_string(1, buffer_subsub);
+        pbf_sub.add_int32(2, 88);
+
+        pbf_test.add_message(1, buffer_sub);
+    }
+
+    SECTION("string with subwriter") {
+        protozero::pbf_writer pbf_sub(pbf_test, 1);
+        {
+            protozero::pbf_writer pbf_subsub(pbf_sub, 1);
+            pbf_subsub.add_string(1, "foobar");
+            pbf_subsub.add_int32(2, 99);
+        }
+        pbf_sub.add_int32(2, 88);
+    }
+
+    pbf_test.add_int32(2, 77);
+
+    protozero::pbf_reader message(buffer_test);
+    check(message);
+}
+
+TEST_CASE("write nested message fields - no message") {
+
+    std::string buffer_test;
+    protozero::pbf_writer pbf_test(buffer_test);
+
+    SECTION("nothing") {
+    }
+
+    SECTION("empty string") {
+        std::string buffer_sub;
+
+        pbf_test.add_message(1, buffer_sub);
+    }
+
+    SECTION("string with pbf_writer") {
+        std::string buffer_sub;
+        protozero::pbf_writer pbf_sub(buffer_sub);
+
+        pbf_test.add_message(1, buffer_sub);
+    }
+
+    SECTION("string with subwriter") {
+        protozero::pbf_writer pbf_sub(pbf_test, 1);
+    }
+
+    pbf_test.add_int32(2, 77);
+
+    protozero::pbf_reader message(buffer_test);
+    check_empty(message);
+}
+
diff --git a/test/t/nested/testcase.cpp b/test/t/nested/testcase.cpp
new file mode 100644
index 0000000..8a11b3f
--- /dev/null
+++ b/test/t/nested/testcase.cpp
@@ -0,0 +1,21 @@
+
+#include <testcase.hpp>
+#include "testcase.pb.h"
+
+int main(int c, char *argv[]) {
+    TestNested::Test msg;
+    msg.set_i(77);
+
+    TestNested::Sub* sub = msg.mutable_sub();
+
+    write_to_file(msg, "data-no-message.pbf");
+
+    sub->set_i(88);
+
+    TestNested::SubSub* subsub = sub->mutable_subsub();
+    subsub->set_s("foobar");
+    subsub->set_i(99);
+
+    write_to_file(msg, "data-message.pbf");
+}
+
diff --git a/test/t/nested/testcase.proto b/test/t/nested/testcase.proto
new file mode 100644
index 0000000..022737b
--- /dev/null
+++ b/test/t/nested/testcase.proto
@@ -0,0 +1,20 @@
+
+option optimize_for = LITE_RUNTIME;
+
+package TestNested;
+
+message SubSub {
+    optional string s = 1;
+    optional int32 i = 2;
+}
+
+message Sub {
+    optional SubSub subsub = 1;
+    optional int32 i = 2;
+}
+
+message Test {
+    optional Sub sub = 1;
+    optional int32 i = 2;
+}
+
diff --git a/test/t/nested/writer_test_cases.cpp b/test/t/nested/writer_test_cases.cpp
new file mode 100644
index 0000000..d2e0490
--- /dev/null
+++ b/test/t/nested/writer_test_cases.cpp
@@ -0,0 +1,46 @@
+
+#include <test.hpp>
+
+#include "test/t/nested/testcase.pb.h"
+
+TEST_CASE("write nested message fields and check with libprotobuf") {
+
+    std::string buffer_test;
+    protozero::pbf_writer pbf_test(buffer_test);
+
+    SECTION("string") {
+        std::string buffer_subsub;
+        protozero::pbf_writer pbf_subsub(buffer_subsub);
+        pbf_subsub.add_string(1, "foobar");
+        pbf_subsub.add_int32(2, 99);
+
+        std::string buffer_sub;
+        protozero::pbf_writer pbf_sub(buffer_sub);
+        pbf_sub.add_string(1, buffer_subsub);
+        pbf_sub.add_int32(2, 88);
+
+        pbf_test.add_message(1, buffer_sub);
+    }
+
+    SECTION("with subwriter") {
+        protozero::pbf_writer pbf_sub(pbf_test, 1);
+        {
+            protozero::pbf_writer pbf_subsub(pbf_sub, 1);
+            pbf_subsub.add_string(1, "foobar");
+            pbf_subsub.add_int32(2, 99);
+        }
+        pbf_sub.add_int32(2, 88);
+    }
+
+    pbf_test.add_int32(2, 77);
+
+    TestNested::Test msg;
+    msg.ParseFromString(buffer_test);
+
+    REQUIRE(msg.i() == 77);
+    REQUIRE(msg.sub().i() == 88);
+    REQUIRE(msg.sub().subsub().i() == 99);
+    REQUIRE(msg.sub().subsub().s() == "foobar");
+
+}
+
diff --git a/test/t/repeated/data-empty.pbf b/test/t/repeated/data-empty.pbf
new file mode 100644
index 0000000..e69de29
diff --git a/test/t/repeated/data-many.pbf b/test/t/repeated/data-many.pbf
new file mode 100644
index 0000000..39476f4
Binary files /dev/null and b/test/t/repeated/data-many.pbf differ
diff --git a/test/t/repeated/data-one.pbf b/test/t/repeated/data-one.pbf
new file mode 100644
index 0000000..268720a
Binary files /dev/null and b/test/t/repeated/data-one.pbf differ
diff --git a/test/t/repeated/test_cases.cpp b/test/t/repeated/test_cases.cpp
new file mode 100644
index 0000000..9a6148c
--- /dev/null
+++ b/test/t/repeated/test_cases.cpp
@@ -0,0 +1,80 @@
+
+#include <test.hpp>
+
+TEST_CASE("read repeated fields") {
+
+    SECTION("empty") {
+        std::string buffer = load_data("repeated/data-empty");
+
+        protozero::pbf_reader item(buffer);
+
+        REQUIRE(!item.next());
+    }
+
+    SECTION("one") {
+        std::string buffer = load_data("repeated/data-one");
+
+        protozero::pbf_reader item(buffer);
+
+        REQUIRE(item.next());
+        REQUIRE(item.get_int32() == 0L);
+        REQUIRE(!item.next());
+    }
+
+    SECTION("many") {
+        std::string buffer = load_data("repeated/data-many");
+
+        protozero::pbf_reader item(buffer);
+
+        REQUIRE(item.next());
+        REQUIRE(item.get_int32() == 0L);
+
+        REQUIRE(item.next());
+        REQUIRE(item.get_int32() == 1L);
+
+        REQUIRE(item.next());
+        REQUIRE(item.get_int32() == -1L);
+
+        REQUIRE(item.next());
+        REQUIRE(item.get_int32() == std::numeric_limits<int32_t>::max());
+
+        REQUIRE(item.next());
+        REQUIRE(item.get_int32() == std::numeric_limits<int32_t>::min());
+
+        REQUIRE(!item.next());
+    }
+
+    SECTION("end_of_buffer") {
+        std::string buffer = load_data("repeated/data-one");
+
+        for (size_t i=1; i < buffer.size(); ++i) {
+            protozero::pbf_reader item(buffer.data(), i);
+            REQUIRE(item.next());
+            REQUIRE_THROWS_AS(item.get_int32(), protozero::end_of_buffer_exception);
+        }
+    }
+
+}
+
+TEST_CASE("write repeated fields") {
+
+    std::string buffer;
+    protozero::pbf_writer pw(buffer);
+
+    SECTION("one") {
+        pw.add_int32(1, 0L);
+        REQUIRE(buffer == load_data("repeated/data-one"));
+    }
+
+    SECTION("many") {
+        pw.add_int32(1, 0L);
+        pw.add_int32(1, 1L);
+        pw.add_int32(1, -1L);
+        pw.add_int32(1, std::numeric_limits<int32_t>::max());
+        pw.add_int32(1, std::numeric_limits<int32_t>::min());
+
+        REQUIRE(buffer == load_data("repeated/data-many"));
+    }
+
+}
+
diff --git a/test/t/repeated/testcase.cpp b/test/t/repeated/testcase.cpp
new file mode 100644
index 0000000..f86525a
--- /dev/null
+++ b/test/t/repeated/testcase.cpp
@@ -0,0 +1,19 @@
+
+#include <testcase.hpp>
+#include "testcase.pb.h"
+
+int main(int c, char *argv[]) {
+    TestRepeated::Test msg;
+
+    write_to_file(msg, "data-empty.pbf");
+
+    msg.add_i(0);
+    write_to_file(msg, "data-one.pbf");
+
+    msg.add_i(1);
+    msg.add_i(-1);
+    msg.add_i(std::numeric_limits<int32_t>::max());
+    msg.add_i(std::numeric_limits<int32_t>::min());
+    write_to_file(msg, "data-many.pbf");
+}
+
diff --git a/test/t/repeated/testcase.proto b/test/t/repeated/testcase.proto
new file mode 100644
index 0000000..a6f95d2
--- /dev/null
+++ b/test/t/repeated/testcase.proto
@@ -0,0 +1,11 @@
+
+option optimize_for = LITE_RUNTIME;
+
+package TestRepeated;
+
+message Test {
+
+    repeated int32 i = 1;
+
+}
+
diff --git a/test/t/repeated/writer_test_cases.cpp b/test/t/repeated/writer_test_cases.cpp
new file mode 100644
index 0000000..90194b0
--- /dev/null
+++ b/test/t/repeated/writer_test_cases.cpp
@@ -0,0 +1,40 @@
+
+#include <test.hpp>
+
+#include "test/t/repeated/testcase.pb.h"
+
+TEST_CASE("write repeated fields and check with libprotobuf") {
+
+    std::string buffer;
+    protozero::pbf_writer pw(buffer);
+
+    TestRepeated::Test msg;
+
+    SECTION("one") {
+        pw.add_int32(1, 0L);
+
+        msg.ParseFromString(buffer);
+
+        REQUIRE(msg.i().size() == 1);
+        REQUIRE(msg.i(0) == 0L);
+    }
+
+    SECTION("many") {
+        pw.add_int32(1, 0L);
+        pw.add_int32(1, 1L);
+        pw.add_int32(1, -1L);
+        pw.add_int32(1, std::numeric_limits<int32_t>::max());
+        pw.add_int32(1, std::numeric_limits<int32_t>::min());
+
+        msg.ParseFromString(buffer);
+
+        REQUIRE(msg.i().size() == 5);
+        REQUIRE(msg.i(0) == 0L);
+        REQUIRE(msg.i(1) == 1L);
+        REQUIRE(msg.i(2) == -1L);
+        REQUIRE(msg.i(3) == std::numeric_limits<int32_t>::max());
+        REQUIRE(msg.i(4) == std::numeric_limits<int32_t>::min());
+    }
+
+}
+
diff --git a/test/t/repeated_packed_bool/data-empty.pbf b/test/t/repeated_packed_bool/data-empty.pbf
new file mode 100644
index 0000000..e69de29
diff --git a/test/t/repeated_packed_bool/data-many.pbf b/test/t/repeated_packed_bool/data-many.pbf
new file mode 100644
index 0000000..0753ca7
Binary files /dev/null and b/test/t/repeated_packed_bool/data-many.pbf differ
diff --git a/test/t/repeated_packed_bool/data-one.pbf b/test/t/repeated_packed_bool/data-one.pbf
new file mode 100644
index 0000000..1607426
--- /dev/null
+++ b/test/t/repeated_packed_bool/data-one.pbf
@@ -0,0 +1,2 @@
+
+
\ No newline at end of file
diff --git a/test/t/repeated_packed_bool/test_cases.cpp b/test/t/repeated_packed_bool/test_cases.cpp
new file mode 100644
index 0000000..7d3424e
--- /dev/null
+++ b/test/t/repeated_packed_bool/test_cases.cpp
@@ -0,0 +1,85 @@
+
+#include <test.hpp>
+
+TEST_CASE("read repeated packed bool field") {
+
+    SECTION("empty") {
+        std::string buffer = load_data("repeated_packed_bool/data-empty");
+
+        protozero::pbf_reader item(buffer);
+
+        REQUIRE(!item.next());
+    }
+
+    SECTION("one") {
+        std::string buffer = load_data("repeated_packed_bool/data-one");
+
+        protozero::pbf_reader item(buffer);
+
+        REQUIRE(item.next());
+        auto it_pair = item.get_packed_bool();
+        REQUIRE(!item.next());
+
+        REQUIRE(it_pair.first != it_pair.second);
+        REQUIRE(*it_pair.first);
+        REQUIRE(++it_pair.first == it_pair.second);
+    }
+
+    SECTION("many") {
+        std::string buffer = load_data("repeated_packed_bool/data-many");
+
+        protozero::pbf_reader item(buffer);
+
+        REQUIRE(item.next());
+        auto it_pair = item.get_packed_bool();
+        REQUIRE(!item.next());
+
+        auto it = it_pair.first;
+        REQUIRE(it != it_pair.second);
+        REQUIRE(*it++);
+        REQUIRE(*it++);
+        REQUIRE(! *it++);
+        REQUIRE(*it++);
+        REQUIRE(it == it_pair.second);
+    }
+
+    SECTION("end_of_buffer") {
+        std::string buffer = load_data("repeated_packed_bool/data-many");
+
+        for (size_t i=1; i < buffer.size(); ++i) {
+            protozero::pbf_reader item(buffer.data(), i);
+            REQUIRE(item.next());
+            REQUIRE_THROWS_AS(item.get_packed_bool(), protozero::end_of_buffer_exception);
+        }
+    }
+
+}
+
+TEST_CASE("write repeated packed bool field") {
+
+    std::string buffer;
+    protozero::pbf_writer pw(buffer);
+
+    SECTION("empty") {
+        bool data[] = { true };
+        pw.add_packed_bool(1, std::begin(data), std::begin(data) /* !!!! */);
+
+        REQUIRE(buffer == load_data("repeated_packed_bool/data-empty"));
+    }
+
+    SECTION("one") {
+        bool data[] = { true };
+        pw.add_packed_bool(1, std::begin(data), std::end(data));
+
+        REQUIRE(buffer == load_data("repeated_packed_bool/data-one"));
+    }
+
+    SECTION("many") {
+        bool data[] = { true, true, false, true };
+        pw.add_packed_bool(1, std::begin(data), std::end(data));
+
+        REQUIRE(buffer == load_data("repeated_packed_bool/data-many"));
+    }
+
+}
+
diff --git a/test/t/repeated_packed_bool/testcase.cpp b/test/t/repeated_packed_bool/testcase.cpp
new file mode 100644
index 0000000..df7b876
--- /dev/null
+++ b/test/t/repeated_packed_bool/testcase.cpp
@@ -0,0 +1,18 @@
+
+#include <testcase.hpp>
+#include "testcase.pb.h"
+
+int main(int c, char *argv[]) {
+    TestRepeatedPackedBool::Test msg;
+
+    write_to_file(msg, "data-empty.pbf");
+
+    msg.add_b(true);
+    write_to_file(msg, "data-one.pbf");
+
+    msg.add_b(true);
+    msg.add_b(false);
+    msg.add_b(true);
+    write_to_file(msg, "data-many.pbf");
+}
+
diff --git a/test/t/repeated_packed_bool/testcase.proto b/test/t/repeated_packed_bool/testcase.proto
new file mode 100644
index 0000000..53428ae
--- /dev/null
+++ b/test/t/repeated_packed_bool/testcase.proto
@@ -0,0 +1,11 @@
+
+option optimize_for = LITE_RUNTIME;
+
+package TestRepeatedPackedBool;
+
+message Test {
+
+    repeated bool b = 1 [packed=true];
+
+}
+
diff --git a/test/t/repeated_packed_double/data-empty.pbf b/test/t/repeated_packed_double/data-empty.pbf
new file mode 100644
index 0000000..e69de29
diff --git a/test/t/repeated_packed_double/data-many.pbf b/test/t/repeated_packed_double/data-many.pbf
new file mode 100644
index 0000000..b5ba733
Binary files /dev/null and b/test/t/repeated_packed_double/data-many.pbf differ
diff --git a/test/t/repeated_packed_double/data-one.pbf b/test/t/repeated_packed_double/data-one.pbf
new file mode 100644
index 0000000..3c9ac4b
--- /dev/null
+++ b/test/t/repeated_packed_double/data-one.pbf
@@ -0,0 +1,3 @@
+
+ףp=
+W1@
\ No newline at end of file
diff --git a/test/t/repeated_packed_double/test_cases.cpp b/test/t/repeated_packed_double/test_cases.cpp
new file mode 100644
index 0000000..7fc7c52
--- /dev/null
+++ b/test/t/repeated_packed_double/test_cases.cpp
@@ -0,0 +1,84 @@
+
+#include <test.hpp>
+
+TEST_CASE("read repeated packed double field") {
+
+    SECTION("empty") {
+        std::string buffer = load_data("repeated_packed_double/data-empty");
+
+        protozero::pbf_reader item(buffer);
+
+        REQUIRE(!item.next());
+    }
+
+    SECTION("one") {
+        std::string buffer = load_data("repeated_packed_double/data-one");
+
+        protozero::pbf_reader item(buffer);
+
+        REQUIRE(item.next());
+        auto it_pair = item.get_packed_double();
+        REQUIRE(!item.next());
+
+        REQUIRE(*it_pair.first == 17.34);
+        REQUIRE(++it_pair.first == it_pair.second);
+    }
+
+    SECTION("many") {
+        std::string buffer = load_data("repeated_packed_double/data-many");
+
+        protozero::pbf_reader item(buffer);
+
+        REQUIRE(item.next());
+        auto it_pair = item.get_packed_double();
+        REQUIRE(!item.next());
+
+        auto it = it_pair.first;
+        REQUIRE(*it++ == 17.34);
+        REQUIRE(*it++ ==   0.0);
+        REQUIRE(*it++ ==   1.0);
+        REQUIRE(*it++ == std::numeric_limits<double>::min());
+        REQUIRE(*it++ == std::numeric_limits<double>::max());
+        REQUIRE(it == it_pair.second);
+    }
+
+    SECTION("end_of_buffer") {
+        std::string buffer = load_data("repeated_packed_double/data-many");
+
+        for (size_t i=1; i < buffer.size(); ++i) {
+            protozero::pbf_reader item(buffer.data(), i);
+            REQUIRE(item.next());
+            REQUIRE_THROWS_AS(item.get_packed_double(), protozero::end_of_buffer_exception);
+        }
+    }
+
+}
+
+TEST_CASE("write repeated packed double field") {
+
+    std::string buffer;
+    protozero::pbf_writer pw(buffer);
+
+    SECTION("empty") {
+        double data[] = { 17.34 };
+        pw.add_packed_double(1, std::begin(data), std::begin(data) /* !!!! */);
+
+        REQUIRE(buffer == load_data("repeated_packed_double/data-empty"));
+    }
+
+    SECTION("one") {
+        double data[] = { 17.34 };
+        pw.add_packed_double(1, std::begin(data), std::end(data));
+
+        REQUIRE(buffer == load_data("repeated_packed_double/data-one"));
+    }
+
+    SECTION("many") {
+        double data[] = { 17.34, 0.0, 1.0, std::numeric_limits<double>::min(), std::numeric_limits<double>::max() };
+        pw.add_packed_double(1, std::begin(data), std::end(data));
+
+        REQUIRE(buffer == load_data("repeated_packed_double/data-many"));
+    }
+
+}
+
diff --git a/test/t/repeated_packed_double/testcase.cpp b/test/t/repeated_packed_double/testcase.cpp
new file mode 100644
index 0000000..21d2e8a
--- /dev/null
+++ b/test/t/repeated_packed_double/testcase.cpp
@@ -0,0 +1,19 @@
+
+#include <testcase.hpp>
+#include "testcase.pb.h"
+
+int main(int c, char *argv[]) {
+    TestRepeatedPackedDouble::Test msg;
+
+    write_to_file(msg, "data-empty.pbf");
+
+    msg.add_d(17.34);
+    write_to_file(msg, "data-one.pbf");
+
+    msg.add_d(0.0);
+    msg.add_d(1.0);
+    msg.add_d(std::numeric_limits<double>::min());
+    msg.add_d(std::numeric_limits<double>::max());
+    write_to_file(msg, "data-many.pbf");
+}
+
diff --git a/test/t/repeated_packed_double/testcase.proto b/test/t/repeated_packed_double/testcase.proto
new file mode 100644
index 0000000..ccb5b24
--- /dev/null
+++ b/test/t/repeated_packed_double/testcase.proto
@@ -0,0 +1,11 @@
+
+option optimize_for = LITE_RUNTIME;
+
+package TestRepeatedPackedDouble;
+
+message Test {
+
+    repeated double d = 1 [packed=true];
+
+}
+
diff --git a/test/t/repeated_packed_enum/data-empty.pbf b/test/t/repeated_packed_enum/data-empty.pbf
new file mode 100644
index 0000000..e69de29
diff --git a/test/t/repeated_packed_enum/data-many.pbf b/test/t/repeated_packed_enum/data-many.pbf
new file mode 100644
index 0000000..c0226e9
Binary files /dev/null and b/test/t/repeated_packed_enum/data-many.pbf differ
diff --git a/test/t/repeated_packed_enum/data-one.pbf b/test/t/repeated_packed_enum/data-one.pbf
new file mode 100644
index 0000000..742c749
Binary files /dev/null and b/test/t/repeated_packed_enum/data-one.pbf differ
diff --git a/test/t/repeated_packed_enum/test_cases.cpp b/test/t/repeated_packed_enum/test_cases.cpp
new file mode 100644
index 0000000..f475a94
--- /dev/null
+++ b/test/t/repeated_packed_enum/test_cases.cpp
@@ -0,0 +1,84 @@
+
+#include <test.hpp>
+
+TEST_CASE("read repeated packed enum field") {
+
+    SECTION("empty") {
+        std::string buffer = load_data("repeated_packed_enum/data-empty");
+
+        protozero::pbf_reader item(buffer);
+
+        REQUIRE(!item.next());
+    }
+
+    SECTION("one") {
+        std::string buffer = load_data("repeated_packed_enum/data-one");
+
+        protozero::pbf_reader item(buffer);
+
+        REQUIRE(item.next());
+        auto it_pair = item.get_packed_enum();
+        REQUIRE(!item.next());
+
+        REQUIRE(it_pair.first != it_pair.second);
+        REQUIRE(*it_pair.first == 0 /* BLACK */);
+        REQUIRE(++it_pair.first == it_pair.second);
+    }
+
+    SECTION("many") {
+        std::string buffer = load_data("repeated_packed_enum/data-many");
+
+        protozero::pbf_reader item(buffer);
+
+        REQUIRE(item.next());
+        auto it_pair = item.get_packed_enum();
+        REQUIRE(!item.next());
+
+        auto it = it_pair.first;
+        REQUIRE(it != it_pair.second);
+        REQUIRE(*it++ == 0 /* BLACK */);
+        REQUIRE(*it++ == 3 /* BLUE */);
+        REQUIRE(*it++ == 2 /* GREEN */);
+        REQUIRE(it == it_pair.second);
+    }
+
+    SECTION("end_of_buffer") {
+        std::string buffer = load_data("repeated_packed_enum/data-many");
+
+        for (size_t i=1; i < buffer.size(); ++i) {
+            protozero::pbf_reader item(buffer.data(), i);
+            REQUIRE(item.next());
+            REQUIRE_THROWS_AS(item.get_packed_enum(), protozero::end_of_buffer_exception);
+        }
+    }
+
+}
+
+TEST_CASE("write repeated packed enum field") {
+
+    std::string buffer;
+    protozero::pbf_writer pw(buffer);
+
+    SECTION("empty") {
+        int32_t data[] = { 0 /* BLACK */ };
+        pw.add_packed_enum(1, std::begin(data), std::begin(data) /* !!!! */);
+
+        REQUIRE(buffer == load_data("repeated_packed_enum/data-empty"));
+    }
+
+    SECTION("one") {
+        int32_t data[] = { 0 /* BLACK */ };
+        pw.add_packed_enum(1, std::begin(data), std::end(data));
+
+        REQUIRE(buffer == load_data("repeated_packed_enum/data-one"));
+    }
+
+    SECTION("many") {
+        int32_t data[] = { 0 /* BLACK */, 3 /* BLUE */, 2 /* GREEN */ };
+        pw.add_packed_enum(1, std::begin(data), std::end(data));
+
+        REQUIRE(buffer == load_data("repeated_packed_enum/data-many"));
+    }
+
+}
+
diff --git a/test/t/repeated_packed_enum/testcase.cpp b/test/t/repeated_packed_enum/testcase.cpp
new file mode 100644
index 0000000..2999953
--- /dev/null
+++ b/test/t/repeated_packed_enum/testcase.cpp
@@ -0,0 +1,17 @@
+
+#include <testcase.hpp>
+#include "testcase.pb.h"
+
+int main(int c, char *argv[]) {
+    TestRepeatedPackedEnum::Test msg;
+
+    write_to_file(msg, "data-empty.pbf");
+
+    msg.add_color(TestRepeatedPackedEnum::BLACK);
+    write_to_file(msg, "data-one.pbf");
+
+    msg.add_color(TestRepeatedPackedEnum::BLUE);
+    msg.add_color(TestRepeatedPackedEnum::GREEN);
+    write_to_file(msg, "data-many.pbf");
+}
+
diff --git a/test/t/repeated_packed_enum/testcase.proto b/test/t/repeated_packed_enum/testcase.proto
new file mode 100644
index 0000000..01e5df3
--- /dev/null
+++ b/test/t/repeated_packed_enum/testcase.proto
@@ -0,0 +1,18 @@
+
+option optimize_for = LITE_RUNTIME;
+
+package TestRepeatedPackedEnum;
+
+enum Color {
+    BLACK = 0;
+    RED   = 1;
+    GREEN = 2;
+    BLUE  = 3;
+}
+
+message Test {
+
+    repeated Color color = 1 [packed=true];
+
+}
+
diff --git a/test/t/repeated_packed_fixed32/data-empty.pbf b/test/t/repeated_packed_fixed32/data-empty.pbf
new file mode 100644
index 0000000..e69de29
diff --git a/test/t/repeated_packed_fixed32/data-many.pbf b/test/t/repeated_packed_fixed32/data-many.pbf
new file mode 100644
index 0000000..dff236c
Binary files /dev/null and b/test/t/repeated_packed_fixed32/data-many.pbf differ
diff --git a/test/t/repeated_packed_fixed32/data-one.pbf b/test/t/repeated_packed_fixed32/data-one.pbf
new file mode 100644
index 0000000..43f147d
Binary files /dev/null and b/test/t/repeated_packed_fixed32/data-one.pbf differ
diff --git a/test/t/repeated_packed_fixed32/test_cases.cpp b/test/t/repeated_packed_fixed32/test_cases.cpp
new file mode 100644
index 0000000..266f310
--- /dev/null
+++ b/test/t/repeated_packed_fixed32/test_cases.cpp
@@ -0,0 +1,120 @@
+
+#include <test.hpp>
+
+TEST_CASE("read repeated packed fixed32 field") {
+
+    SECTION("empty") {
+        std::string buffer = load_data("repeated_packed_fixed32/data-empty");
+
+        protozero::pbf_reader item(buffer);
+
+        REQUIRE(!item.next());
+    }
+
+    SECTION("one") {
+        std::string buffer = load_data("repeated_packed_fixed32/data-one");
+
+        protozero::pbf_reader item(buffer);
+
+        REQUIRE(item.next());
+        auto it_pair = item.get_packed_fixed32();
+        REQUIRE(!item.next());
+
+        REQUIRE(*it_pair.first == 17UL);
+        REQUIRE(++it_pair.first == it_pair.second);
+    }
+
+    SECTION("many") {
+        std::string buffer = load_data("repeated_packed_fixed32/data-many");
+
+        protozero::pbf_reader item(buffer);
+
+        REQUIRE(item.next());
+        auto it_pair = item.get_packed_fixed32();
+        REQUIRE(!item.next());
+
+        auto it = it_pair.first;
+        REQUIRE(*it++ == 17UL);
+        REQUIRE(*it++ ==  0UL);
+        REQUIRE(*it++ ==  1UL);
+        REQUIRE(*it++ == std::numeric_limits<uint32_t>::max());
+        REQUIRE(it == it_pair.second);
+    }
+
+    SECTION("end_of_buffer") {
+        std::string buffer = load_data("repeated_packed_fixed32/data-many");
+
+        for (size_t i=1; i < buffer.size(); ++i) {
+            protozero::pbf_reader item(buffer.data(), i);
+            REQUIRE(item.next());
+            REQUIRE_THROWS_AS(item.get_packed_fixed32(), protozero::end_of_buffer_exception);
+        }
+    }
+
+}
+
+TEST_CASE("write repeated packed fixed32 field") {
+
+    std::string buffer;
+    protozero::pbf_writer pw(buffer);
+
+    SECTION("empty") {
+        uint32_t data[] = { 17UL };
+        pw.add_packed_fixed32(1, std::begin(data), std::begin(data) /* !!!! */);
+
+        REQUIRE(buffer == load_data("repeated_packed_fixed32/data-empty"));
+    }
+
+    SECTION("one") {
+        uint32_t data[] = { 17UL };
+        pw.add_packed_fixed32(1, std::begin(data), std::end(data));
+
+        REQUIRE(buffer == load_data("repeated_packed_fixed32/data-one"));
+    }
+
+    SECTION("many") {
+        uint32_t data[] = { 17UL, 0UL, 1UL, std::numeric_limits<uint32_t>::max() };
+        pw.add_packed_fixed32(1, std::begin(data), std::end(data));
+
+        REQUIRE(buffer == load_data("repeated_packed_fixed32/data-many"));
+    }
+
+}
+
+TEST_CASE("write from different types of iterators") {
+
+    std::string buffer;
+    protozero::pbf_writer pw(buffer);
+
+    SECTION("from uint16_t") {
+        uint16_t data[] = { 1, 4, 9, 16, 25 };
+
+        pw.add_packed_fixed32(1, std::begin(data), std::end(data));
+    }
+
+    SECTION("from string") {
+        std::string data = "1 4 9 16 25";
+        std::stringstream sdata(data);
+
+        std::istream_iterator<uint32_t> eod;
+        std::istream_iterator<uint32_t> it(sdata);
+
+        pw.add_packed_fixed32(1, it, eod);
+    }
+
+    protozero::pbf_reader item(buffer);
+
+    REQUIRE(item.next());
+    auto it_pair = item.get_packed_fixed32();
+    REQUIRE(!item.next());
+    REQUIRE(std::distance(it_pair.first, it_pair.second) == 5);
+
+    auto i = it_pair.first;
+    REQUIRE(*i++ ==  1);
+    REQUIRE(*i++ ==  4);
+    REQUIRE(*i++ ==  9);
+    REQUIRE(*i++ == 16);
+    REQUIRE(*i++ == 25);
+
+}
+
diff --git a/test/t/repeated_packed_fixed32/testcase.cpp b/test/t/repeated_packed_fixed32/testcase.cpp
new file mode 100644
index 0000000..34237a7
--- /dev/null
+++ b/test/t/repeated_packed_fixed32/testcase.cpp
@@ -0,0 +1,18 @@
+
+#include <testcase.hpp>
+#include "testcase.pb.h"
+
+int main(int c, char *argv[]) {
+    TestRepeatedPackedFixed32::Test msg;
+
+    write_to_file(msg, "data-empty.pbf");
+
+    msg.add_i(17UL);
+    write_to_file(msg, "data-one.pbf");
+
+    msg.add_i(0UL);
+    msg.add_i(1UL);
+    msg.add_i(std::numeric_limits<uint32_t>::max());
+    write_to_file(msg, "data-many.pbf");
+}
+
diff --git a/test/t/repeated_packed_fixed32/testcase.proto b/test/t/repeated_packed_fixed32/testcase.proto
new file mode 100644
index 0000000..64f258e
--- /dev/null
+++ b/test/t/repeated_packed_fixed32/testcase.proto
@@ -0,0 +1,11 @@
+
+option optimize_for = LITE_RUNTIME;
+
+package TestRepeatedPackedFixed32;
+
+message Test {
+
+    repeated fixed32 i = 1 [packed=true];
+
+}
+
diff --git a/test/t/repeated_packed_fixed32/writer_test_cases.cpp b/test/t/repeated_packed_fixed32/writer_test_cases.cpp
new file mode 100644
index 0000000..2d49c0f
--- /dev/null
+++ b/test/t/repeated_packed_fixed32/writer_test_cases.cpp
@@ -0,0 +1,79 @@
+
+#include <test.hpp>
+
+#include "test/t/repeated_packed_fixed32/testcase.pb.h"
+
+TEST_CASE("write repeated packed fixed32 field and check with libprotobuf") {
+
+    std::string buffer;
+    protozero::pbf_writer pw(buffer);
+
+    TestRepeatedPackedFixed32::Test msg;
+
+    SECTION("empty") {
+        uint32_t data[] = { 17UL };
+        pw.add_packed_fixed32(1, std::begin(data), std::begin(data) /* !!!! */);
+
+        msg.ParseFromString(buffer);
+
+        REQUIRE(msg.i().size() == 0);
+    }
+
+    SECTION("one") {
+        uint32_t data[] = { 17UL };
+        pw.add_packed_fixed32(1, std::begin(data), std::end(data));
+
+        msg.ParseFromString(buffer);
+
+        REQUIRE(msg.i().size() == 1);
+        REQUIRE(msg.i(0) == 17UL);
+    }
+
+    SECTION("many") {
+        uint32_t data[] = { 17UL, 0UL, 1UL, std::numeric_limits<uint32_t>::max() };
+        pw.add_packed_fixed32(1, std::begin(data), std::end(data));
+
+        msg.ParseFromString(buffer);
+
+        REQUIRE(msg.i().size() == 4);
+        REQUIRE(msg.i(0) == 17UL);
+        REQUIRE(msg.i(1) ==  0UL);
+        REQUIRE(msg.i(2) ==  1UL);
+        REQUIRE(msg.i(3) == std::numeric_limits<uint32_t>::max());
+    }
+
+}
+
+TEST_CASE("write from different types of iterators and check with libprotobuf") {
+
+    std::string buffer;
+    protozero::pbf_writer pw(buffer);
+
+    TestRepeatedPackedFixed32::Test msg;
+
+    SECTION("from uint16_t") {
+        uint16_t data[] = { 1, 4, 9, 16, 25 };
+
+        pw.add_packed_fixed32(1, std::begin(data), std::end(data));
+    }
+
+    SECTION("from string") {
+        std::string data = "1 4 9 16 25";
+        std::stringstream sdata(data);
+
+        std::istream_iterator<uint32_t> eod;
+        std::istream_iterator<uint32_t> it(sdata);
+
+        pw.add_packed_fixed32(1, it, eod);
+    }
+
+    msg.ParseFromString(buffer);
+
+    REQUIRE(msg.i().size() == 5);
+    REQUIRE(msg.i(0) ==  1);
+    REQUIRE(msg.i(1) ==  4);
+    REQUIRE(msg.i(2) ==  9);
+    REQUIRE(msg.i(3) == 16);
+    REQUIRE(msg.i(4) == 25);
+}
+
diff --git a/test/t/repeated_packed_fixed64/data-empty.pbf b/test/t/repeated_packed_fixed64/data-empty.pbf
new file mode 100644
index 0000000..e69de29
diff --git a/test/t/repeated_packed_fixed64/data-many.pbf b/test/t/repeated_packed_fixed64/data-many.pbf
new file mode 100644
index 0000000..dc5cd98
Binary files /dev/null and b/test/t/repeated_packed_fixed64/data-many.pbf differ
diff --git a/test/t/repeated_packed_fixed64/data-one.pbf b/test/t/repeated_packed_fixed64/data-one.pbf
new file mode 100644
index 0000000..e45639d
Binary files /dev/null and b/test/t/repeated_packed_fixed64/data-one.pbf differ
diff --git a/test/t/repeated_packed_fixed64/test_cases.cpp b/test/t/repeated_packed_fixed64/test_cases.cpp
new file mode 100644
index 0000000..5bb59d5
--- /dev/null
+++ b/test/t/repeated_packed_fixed64/test_cases.cpp
@@ -0,0 +1,83 @@
+
+#include <test.hpp>
+
+TEST_CASE("read repeated packed fixed64 field") {
+
+    SECTION("empty") {
+        std::string buffer = load_data("repeated_packed_fixed64/data-empty");
+
+        protozero::pbf_reader item(buffer);
+
+        REQUIRE(!item.next());
+    }
+
+    SECTION("one") {
+        std::string buffer = load_data("repeated_packed_fixed64/data-one");
+
+        protozero::pbf_reader item(buffer);
+
+        REQUIRE(item.next());
+        auto it_pair = item.get_packed_fixed64();
+        REQUIRE(!item.next());
+
+        REQUIRE(*it_pair.first == 17ULL);
+        REQUIRE(++it_pair.first == it_pair.second);
+    }
+
+    SECTION("many") {
+        std::string buffer = load_data("repeated_packed_fixed64/data-many");
+
+        protozero::pbf_reader item(buffer);
+
+        REQUIRE(item.next());
+        auto it_pair = item.get_packed_fixed64();
+        REQUIRE(!item.next());
+
+        auto it = it_pair.first;
+        REQUIRE(*it++ == 17ULL);
+        REQUIRE(*it++ ==  0ULL);
+        REQUIRE(*it++ ==  1ULL);
+        REQUIRE(*it++ == std::numeric_limits<uint64_t>::max());
+        REQUIRE(it == it_pair.second);
+    }
+
+    SECTION("end_of_buffer") {
+        std::string buffer = load_data("repeated_packed_fixed64/data-many");
+
+        for (size_t i=1; i < buffer.size(); ++i) {
+            protozero::pbf_reader item(buffer.data(), i);
+            REQUIRE(item.next());
+            REQUIRE_THROWS_AS(item.get_packed_fixed64(), protozero::end_of_buffer_exception);
+        }
+    }
+
+}
+
+TEST_CASE("write repeated packed fixed64 field") {
+
+    std::string buffer;
+    protozero::pbf_writer pw(buffer);
+
+    SECTION("empty") {
+        uint64_t data[] = { 17ULL };
+        pw.add_packed_fixed64(1, std::begin(data), std::begin(data) /* !!!! */);
+
+        REQUIRE(buffer == load_data("repeated_packed_fixed64/data-empty"));
+    }
+
+    SECTION("one") {
+        uint64_t data[] = { 17ULL };
+        pw.add_packed_fixed64(1, std::begin(data), std::end(data));
+
+        REQUIRE(buffer == load_data("repeated_packed_fixed64/data-one"));
+    }
+
+    SECTION("many") {
+        uint64_t data[] = { 17ULL, 0ULL, 1ULL, std::numeric_limits<uint64_t>::max() };
+        pw.add_packed_fixed64(1, std::begin(data), std::end(data));
+
+        REQUIRE(buffer == load_data("repeated_packed_fixed64/data-many"));
+    }
+
+}
+
diff --git a/test/t/repeated_packed_fixed64/testcase.cpp b/test/t/repeated_packed_fixed64/testcase.cpp
new file mode 100644
index 0000000..2fc414a
--- /dev/null
+++ b/test/t/repeated_packed_fixed64/testcase.cpp
@@ -0,0 +1,18 @@
+
+#include <testcase.hpp>
+#include "testcase.pb.h"
+
+int main(int c, char *argv[]) {
+    TestRepeatedPackedFixed64::Test msg;
+
+    write_to_file(msg, "data-empty.pbf");
+
+    msg.add_i(17ULL);
+    write_to_file(msg, "data-one.pbf");
+
+    msg.add_i(0ULL);
+    msg.add_i(1ULL);
+    msg.add_i(std::numeric_limits<uint64_t>::max());
+    write_to_file(msg, "data-many.pbf");
+}
+
diff --git a/test/t/repeated_packed_fixed64/testcase.proto b/test/t/repeated_packed_fixed64/testcase.proto
new file mode 100644
index 0000000..13a954b
--- /dev/null
+++ b/test/t/repeated_packed_fixed64/testcase.proto
@@ -0,0 +1,11 @@
+
+option optimize_for = LITE_RUNTIME;
+
+package TestRepeatedPackedFixed64;
+
+message Test {
+
+    repeated fixed64 i = 1 [packed=true];
+
+}
+
diff --git a/test/t/repeated_packed_float/data-empty.pbf b/test/t/repeated_packed_float/data-empty.pbf
new file mode 100644
index 0000000..e69de29
diff --git a/test/t/repeated_packed_float/data-many.pbf b/test/t/repeated_packed_float/data-many.pbf
new file mode 100644
index 0000000..1f685d4
Binary files /dev/null and b/test/t/repeated_packed_float/data-many.pbf differ
diff --git a/test/t/repeated_packed_float/data-one.pbf b/test/t/repeated_packed_float/data-one.pbf
new file mode 100644
index 0000000..cd8139b
--- /dev/null
+++ b/test/t/repeated_packed_float/data-one.pbf
@@ -0,0 +1,2 @@
+
+R��A
\ No newline at end of file
diff --git a/test/t/repeated_packed_float/test_cases.cpp b/test/t/repeated_packed_float/test_cases.cpp
new file mode 100644
index 0000000..ddbc5fa
--- /dev/null
+++ b/test/t/repeated_packed_float/test_cases.cpp
@@ -0,0 +1,84 @@
+
+#include <test.hpp>
+
+TEST_CASE("read repeated packed float field") {
+
+    SECTION("empty") {
+        std::string buffer = load_data("repeated_packed_float/data-empty");
+
+        protozero::pbf_reader item(buffer);
+
+        REQUIRE(!item.next());
+    }
+
+    SECTION("one") {
+        std::string buffer = load_data("repeated_packed_float/data-one");
+
+        protozero::pbf_reader item(buffer);
+
+        REQUIRE(item.next());
+        auto it_pair = item.get_packed_float();
+        REQUIRE(!item.next());
+
+        REQUIRE(*it_pair.first == 17.34f);
+        REQUIRE(++it_pair.first == it_pair.second);
+    }
+
+    SECTION("many") {
+        std::string buffer = load_data("repeated_packed_float/data-many");
+
+        protozero::pbf_reader item(buffer);
+
+        REQUIRE(item.next());
+        auto it_pair = item.get_packed_float();
+        REQUIRE(!item.next());
+
+        auto it = it_pair.first;
+        REQUIRE(*it++ == 17.34f);
+        REQUIRE(*it++ ==   0.0f);
+        REQUIRE(*it++ ==   1.0f);
+        REQUIRE(*it++ == std::numeric_limits<float>::min());
+        REQUIRE(*it++ == std::numeric_limits<float>::max());
+        REQUIRE(it == it_pair.second);
+    }
+
+    SECTION("end_of_buffer") {
+        std::string buffer = load_data("repeated_packed_float/data-many");
+
+        for (size_t i=1; i < buffer.size(); ++i) {
+            protozero::pbf_reader item(buffer.data(), i);
+            REQUIRE(item.next());
+            REQUIRE_THROWS_AS(item.get_packed_float(), protozero::end_of_buffer_exception);
+        }
+    }
+
+}
+
+TEST_CASE("write repeated packed float field") {
+
+    std::string buffer;
+    protozero::pbf_writer pw(buffer);
+
+    SECTION("empty") {
+        float data[] = { 17.34f };
+        pw.add_packed_float(1, std::begin(data), std::begin(data) /* !!!! */);
+
+        REQUIRE(buffer == load_data("repeated_packed_float/data-empty"));
+    }
+
+    SECTION("one") {
+        float data[] = { 17.34f };
+        pw.add_packed_float(1, std::begin(data), std::end(data));
+
+        REQUIRE(buffer == load_data("repeated_packed_float/data-one"));
+    }
+
+    SECTION("many") {
+        float data[] = { 17.34f, 0.0f, 1.0f, std::numeric_limits<float>::min(), std::numeric_limits<float>::max() };
+        pw.add_packed_float(1, std::begin(data), std::end(data));
+
+        REQUIRE(buffer == load_data("repeated_packed_float/data-many"));
+    }
+
+}
+
diff --git a/test/t/repeated_packed_float/testcase.cpp b/test/t/repeated_packed_float/testcase.cpp
new file mode 100644
index 0000000..6498a70
--- /dev/null
+++ b/test/t/repeated_packed_float/testcase.cpp
@@ -0,0 +1,19 @@
+
+#include <testcase.hpp>
+#include "testcase.pb.h"
+
+int main(int c, char *argv[]) {
+    TestRepeatedPackedFloat::Test msg;
+
+    write_to_file(msg, "data-empty.pbf");
+
+    msg.add_f(17.34f);
+    write_to_file(msg, "data-one.pbf");
+
+    msg.add_f(0.0f);
+    msg.add_f(1.0f);
+    msg.add_f(std::numeric_limits<float>::min());
+    msg.add_f(std::numeric_limits<float>::max());
+    write_to_file(msg, "data-many.pbf");
+}
+
diff --git a/test/t/repeated_packed_float/testcase.proto b/test/t/repeated_packed_float/testcase.proto
new file mode 100644
index 0000000..1e85ab0
--- /dev/null
+++ b/test/t/repeated_packed_float/testcase.proto
@@ -0,0 +1,11 @@
+
+option optimize_for = LITE_RUNTIME;
+
+package TestRepeatedPackedFloat;
+
+message Test {
+
+    repeated float f = 1 [packed=true];
+
+}
+
diff --git a/test/t/repeated_packed_int32/data-empty.pbf b/test/t/repeated_packed_int32/data-empty.pbf
new file mode 100644
index 0000000..e69de29
diff --git a/test/t/repeated_packed_int32/data-many.pbf b/test/t/repeated_packed_int32/data-many.pbf
new file mode 100644
index 0000000..1b28de6
Binary files /dev/null and b/test/t/repeated_packed_int32/data-many.pbf differ
diff --git a/test/t/repeated_packed_int32/data-one.pbf b/test/t/repeated_packed_int32/data-one.pbf
new file mode 100644
index 0000000..7f9c1a6
--- /dev/null
+++ b/test/t/repeated_packed_int32/data-one.pbf
@@ -0,0 +1,2 @@
+
+
\ No newline at end of file
diff --git a/test/t/repeated_packed_int32/test_cases.cpp b/test/t/repeated_packed_int32/test_cases.cpp
new file mode 100644
index 0000000..dc369a2
--- /dev/null
+++ b/test/t/repeated_packed_int32/test_cases.cpp
@@ -0,0 +1,87 @@
+
+#include <test.hpp>
+
+TEST_CASE("read repeated packed int32 field") {
+
+    SECTION("empty") {
+        std::string buffer = load_data("repeated_packed_int32/data-empty");
+
+        protozero::pbf_reader item(buffer);
+
+        REQUIRE(!item.next());
+    }
+
+    SECTION("one") {
+        std::string buffer = load_data("repeated_packed_int32/data-one");
+
+        protozero::pbf_reader item(buffer);
+
+        REQUIRE(item.next());
+        auto it_pair = item.get_packed_int32();
+        REQUIRE(!item.next());
+
+        REQUIRE(it_pair.first != it_pair.second);
+        REQUIRE(*it_pair.first == 17L);
+        REQUIRE(++it_pair.first == it_pair.second);
+    }
+
+    SECTION("many") {
+        std::string buffer = load_data("repeated_packed_int32/data-many");
+
+        protozero::pbf_reader item(buffer);
+
+        REQUIRE(item.next());
+        auto it_pair = item.get_packed_int32();
+        REQUIRE(!item.next());
+
+        auto it = it_pair.first;
+        REQUIRE(it != it_pair.second);
+        REQUIRE(*it++ == 17L);
+        REQUIRE(*it++ ==  0L);
+        REQUIRE(*it++ ==  1L);
+        REQUIRE(*it++ ==  -1L);
+        REQUIRE(*it++ == std::numeric_limits<int32_t>::max());
+        REQUIRE(*it++ == std::numeric_limits<int32_t>::min());
+        REQUIRE(it == it_pair.second);
+    }
+
+    SECTION("end_of_buffer") {
+        std::string buffer = load_data("repeated_packed_int32/data-many");
+
+        for (size_t i=1; i < buffer.size(); ++i) {
+            protozero::pbf_reader item(buffer.data(), i);
+            REQUIRE(item.next());
+            REQUIRE_THROWS_AS(item.get_packed_int32(), protozero::end_of_buffer_exception);
+        }
+    }
+
+}
+
+TEST_CASE("write repeated packed int32 field") {
+
+    std::string buffer;
+    protozero::pbf_writer pw(buffer);
+
+    SECTION("empty") {
+        int32_t data[] = { 17L };
+        pw.add_packed_int32(1, std::begin(data), std::begin(data) /* !!!! */);
+
+        REQUIRE(buffer == load_data("repeated_packed_int32/data-empty"));
+    }
+
+    SECTION("one") {
+        int32_t data[] = { 17L };
+        pw.add_packed_int32(1, std::begin(data), std::end(data));
+
+        REQUIRE(buffer == load_data("repeated_packed_int32/data-one"));
+    }
+
+    SECTION("many") {
+        int32_t data[] = { 17L, 0L, 1L, -1L, std::numeric_limits<int32_t>::max(), std::numeric_limits<int32_t>::min() };
+        pw.add_packed_int32(1, std::begin(data), std::end(data));
+
+        REQUIRE(buffer == load_data("repeated_packed_int32/data-many"));
+    }
+
+}
+
diff --git a/test/t/repeated_packed_int32/testcase.cpp b/test/t/repeated_packed_int32/testcase.cpp
new file mode 100644
index 0000000..033aa72
--- /dev/null
+++ b/test/t/repeated_packed_int32/testcase.cpp
@@ -0,0 +1,20 @@
+
+#include <testcase.hpp>
+#include "testcase.pb.h"
+
+int main(int c, char *argv[]) {
+    TestRepeatedPackedInt32::Test msg;
+
+    write_to_file(msg, "data-empty.pbf");
+
+    msg.add_i(17L);
+    write_to_file(msg, "data-one.pbf");
+
+    msg.add_i(0L);
+    msg.add_i(1L);
+    msg.add_i(-1L);
+    msg.add_i(std::numeric_limits<int32_t>::max());
+    msg.add_i(std::numeric_limits<int32_t>::min());
+    write_to_file(msg, "data-many.pbf");
+}
+
diff --git a/test/t/repeated_packed_int32/testcase.proto b/test/t/repeated_packed_int32/testcase.proto
new file mode 100644
index 0000000..beeceed
--- /dev/null
+++ b/test/t/repeated_packed_int32/testcase.proto
@@ -0,0 +1,11 @@
+
+option optimize_for = LITE_RUNTIME;
+
+package TestRepeatedPackedInt32;
+
+message Test {
+
+    repeated int32 i = 1 [packed=true];
+
+}
+
diff --git a/test/t/repeated_packed_int64/data-empty.pbf b/test/t/repeated_packed_int64/data-empty.pbf
new file mode 100644
index 0000000..e69de29
diff --git a/test/t/repeated_packed_int64/data-many.pbf b/test/t/repeated_packed_int64/data-many.pbf
new file mode 100644
index 0000000..1a7a7f6
Binary files /dev/null and b/test/t/repeated_packed_int64/data-many.pbf differ
diff --git a/test/t/repeated_packed_int64/data-one.pbf b/test/t/repeated_packed_int64/data-one.pbf
new file mode 100644
index 0000000..7f9c1a6
--- /dev/null
+++ b/test/t/repeated_packed_int64/data-one.pbf
@@ -0,0 +1,2 @@
+
+
\ No newline at end of file
diff --git a/test/t/repeated_packed_int64/test_cases.cpp b/test/t/repeated_packed_int64/test_cases.cpp
new file mode 100644
index 0000000..98dfb6a
--- /dev/null
+++ b/test/t/repeated_packed_int64/test_cases.cpp
@@ -0,0 +1,85 @@
+
+#include <test.hpp>
+
+TEST_CASE("read repeated packed int64 field") {
+
+    SECTION("empty") {
+        std::string buffer = load_data("repeated_packed_int64/data-empty");
+
+        protozero::pbf_reader item(buffer);
+
+        REQUIRE(!item.next());
+    }
+
+    SECTION("one") {
+        std::string buffer = load_data("repeated_packed_int64/data-one");
+
+        protozero::pbf_reader item(buffer);
+
+        REQUIRE(item.next());
+        auto it_pair = item.get_packed_int64();
+        REQUIRE(!item.next());
+
+        REQUIRE(*it_pair.first == 17LL);
+        REQUIRE(++it_pair.first == it_pair.second);
+    }
+
+    SECTION("many") {
+        std::string buffer = load_data("repeated_packed_int64/data-many");
+
+        protozero::pbf_reader item(buffer);
+
+        REQUIRE(item.next());
+        auto it_pair = item.get_packed_int64();
+        REQUIRE(!item.next());
+
+        auto it = it_pair.first;
+        REQUIRE(*it++ == 17LL);
+        REQUIRE(*it++ ==  0LL);
+        REQUIRE(*it++ ==  1LL);
+        REQUIRE(*it++ == -1LL);
+        REQUIRE(*it++ == std::numeric_limits<int64_t>::max());
+        REQUIRE(*it++ == std::numeric_limits<int64_t>::min());
+        REQUIRE(it == it_pair.second);
+    }
+
+    SECTION("end_of_buffer") {
+        std::string buffer = load_data("repeated_packed_int64/data-many");
+
+        for (size_t i=1; i < buffer.size(); ++i) {
+            protozero::pbf_reader item(buffer.data(), i);
+            REQUIRE(item.next());
+            REQUIRE_THROWS_AS(item.get_packed_int64(), protozero::end_of_buffer_exception);
+        }
+    }
+
+}
+
+TEST_CASE("write repeated packed int64 field") {
+
+    std::string buffer;
+    protozero::pbf_writer pw(buffer);
+
+    SECTION("empty") {
+        int64_t data[] = { 17LL };
+        pw.add_packed_int64(1, std::begin(data), std::begin(data) /* !!!! */);
+
+        REQUIRE(buffer == load_data("repeated_packed_int64/data-empty"));
+    }
+
+    SECTION("one") {
+        int64_t data[] = { 17LL };
+        pw.add_packed_int64(1, std::begin(data), std::end(data));
+
+        REQUIRE(buffer == load_data("repeated_packed_int64/data-one"));
+    }
+
+    SECTION("many") {
+        int64_t data[] = { 17LL, 0LL, 1LL, -1LL, std::numeric_limits<int64_t>::max(), std::numeric_limits<int64_t>::min() };
+        pw.add_packed_int64(1, std::begin(data), std::end(data));
+
+        REQUIRE(buffer == load_data("repeated_packed_int64/data-many"));
+    }
+
+}
+
diff --git a/test/t/repeated_packed_int64/testcase.cpp b/test/t/repeated_packed_int64/testcase.cpp
new file mode 100644
index 0000000..e918504
--- /dev/null
+++ b/test/t/repeated_packed_int64/testcase.cpp
@@ -0,0 +1,20 @@
+
+#include <testcase.hpp>
+#include "testcase.pb.h"
+
+int main(int c, char *argv[]) {
+    TestRepeatedPackedInt64::Test msg;
+
+    write_to_file(msg, "data-empty.pbf");
+
+    msg.add_i(17LL);
+    write_to_file(msg, "data-one.pbf");
+
+    msg.add_i(0LL);
+    msg.add_i(1LL);
+    msg.add_i(-1LL);
+    msg.add_i(std::numeric_limits<int64_t>::max());
+    msg.add_i(std::numeric_limits<int64_t>::min());
+    write_to_file(msg, "data-many.pbf");
+}
+
diff --git a/test/t/repeated_packed_int64/testcase.proto b/test/t/repeated_packed_int64/testcase.proto
new file mode 100644
index 0000000..73102c6
--- /dev/null
+++ b/test/t/repeated_packed_int64/testcase.proto
@@ -0,0 +1,11 @@
+
+option optimize_for = LITE_RUNTIME;
+
+package TestRepeatedPackedInt64;
+
+message Test {
+
+    repeated int64 i = 1 [packed=true];
+
+}
+
diff --git a/test/t/repeated_packed_sfixed32/data-empty.pbf b/test/t/repeated_packed_sfixed32/data-empty.pbf
new file mode 100644
index 0000000..e69de29
diff --git a/test/t/repeated_packed_sfixed32/data-many.pbf b/test/t/repeated_packed_sfixed32/data-many.pbf
new file mode 100644
index 0000000..b9ee1b4
Binary files /dev/null and b/test/t/repeated_packed_sfixed32/data-many.pbf differ
diff --git a/test/t/repeated_packed_sfixed32/data-one.pbf b/test/t/repeated_packed_sfixed32/data-one.pbf
new file mode 100644
index 0000000..43f147d
Binary files /dev/null and b/test/t/repeated_packed_sfixed32/data-one.pbf differ
diff --git a/test/t/repeated_packed_sfixed32/test_cases.cpp b/test/t/repeated_packed_sfixed32/test_cases.cpp
new file mode 100644
index 0000000..2ccf3cb
--- /dev/null
+++ b/test/t/repeated_packed_sfixed32/test_cases.cpp
@@ -0,0 +1,86 @@
+
+#include <test.hpp>
+
+TEST_CASE("read repeated packed sfixed32 field") {
+
+    SECTION("empty") {
+        std::string buffer = load_data("repeated_packed_sfixed32/data-empty");
+
+        protozero::pbf_reader item(buffer);
+
+        REQUIRE(!item.next());
+    }
+
+    SECTION("one") {
+        std::string buffer = load_data("repeated_packed_sfixed32/data-one");
+
+        protozero::pbf_reader item(buffer);
+
+        REQUIRE(item.next());
+        auto it_pair = item.get_packed_sfixed32();
+        REQUIRE(!item.next());
+
+        REQUIRE(*it_pair.first == 17L);
+        REQUIRE(++it_pair.first == it_pair.second);
+    }
+
+    SECTION("many") {
+        std::string buffer = load_data("repeated_packed_sfixed32/data-many");
+
+        protozero::pbf_reader item(buffer);
+
+        REQUIRE(item.next());
+        auto it_pair = item.get_packed_sfixed32();
+        REQUIRE(!item.next());
+
+        auto it = it_pair.first;
+        REQUIRE(*it++ == 17L);
+        REQUIRE(*it++ ==  0L);
+        REQUIRE(*it++ ==  1L);
+        REQUIRE(*it++ == -1L);
+        REQUIRE(*it++ == std::numeric_limits<int32_t>::max());
+        REQUIRE(*it++ == std::numeric_limits<int32_t>::min());
+        REQUIRE(it == it_pair.second);
+    }
+
+    SECTION("end_of_buffer") {
+        std::string buffer = load_data("repeated_packed_sfixed32/data-many");
+
+        for (size_t i=1; i < buffer.size(); ++i) {
+            protozero::pbf_reader item(buffer.data(), i);
+            REQUIRE(item.next());
+            REQUIRE_THROWS_AS(item.get_packed_sfixed32(), protozero::end_of_buffer_exception);
+        }
+    }
+
+}
+
+TEST_CASE("write repeated packed sfixed32 field") {
+
+    std::string buffer;
+    protozero::pbf_writer pw(buffer);
+
+    SECTION("empty") {
+        int32_t data[] = { 17L };
+        pw.add_packed_sfixed32(1, std::begin(data), std::begin(data) /* !!!! */);
+
+        REQUIRE(buffer == load_data("repeated_packed_sfixed32/data-empty"));
+    }
+
+    SECTION("one") {
+        int32_t data[] = { 17L };
+        pw.add_packed_sfixed32(1, std::begin(data), std::end(data));
+
+        REQUIRE(buffer == load_data("repeated_packed_sfixed32/data-one"));
+    }
+
+    SECTION("many") {
+        int32_t data[] = { 17L, 0L, 1L, -1L, std::numeric_limits<int32_t>::max(), std::numeric_limits<int32_t>::min() };
+
+        pw.add_packed_sfixed32(1, std::begin(data), std::end(data));
+
+        REQUIRE(buffer == load_data("repeated_packed_sfixed32/data-many"));
+    }
+
+}
+
diff --git a/test/t/repeated_packed_sfixed32/testcase.cpp b/test/t/repeated_packed_sfixed32/testcase.cpp
new file mode 100644
index 0000000..5177b74
--- /dev/null
+++ b/test/t/repeated_packed_sfixed32/testcase.cpp
@@ -0,0 +1,20 @@
+
+#include <testcase.hpp>
+#include "testcase.pb.h"
+
+int main(int c, char *argv[]) {
+    TestRepeatedPackedSFixed32::Test msg;
+
+    write_to_file(msg, "data-empty.pbf");
+
+    msg.add_i(17);
+    write_to_file(msg, "data-one.pbf");
+
+    msg.add_i(0);
+    msg.add_i(1);
+    msg.add_i(-1);
+    msg.add_i(std::numeric_limits<int32_t>::max());
+    msg.add_i(std::numeric_limits<int32_t>::min());
+    write_to_file(msg, "data-many.pbf");
+}
+
diff --git a/test/t/repeated_packed_sfixed32/testcase.proto b/test/t/repeated_packed_sfixed32/testcase.proto
new file mode 100644
index 0000000..c49c61c
--- /dev/null
+++ b/test/t/repeated_packed_sfixed32/testcase.proto
@@ -0,0 +1,11 @@
+
+option optimize_for = LITE_RUNTIME;
+
+package TestRepeatedPackedSFixed32;
+
+message Test {
+
+    repeated sfixed32 i = 1 [packed=true];
+
+}
+
diff --git a/test/t/repeated_packed_sfixed64/data-empty.pbf b/test/t/repeated_packed_sfixed64/data-empty.pbf
new file mode 100644
index 0000000..e69de29
diff --git a/test/t/repeated_packed_sfixed64/data-many.pbf b/test/t/repeated_packed_sfixed64/data-many.pbf
new file mode 100644
index 0000000..9ae5254
Binary files /dev/null and b/test/t/repeated_packed_sfixed64/data-many.pbf differ
diff --git a/test/t/repeated_packed_sfixed64/data-one.pbf b/test/t/repeated_packed_sfixed64/data-one.pbf
new file mode 100644
index 0000000..e45639d
Binary files /dev/null and b/test/t/repeated_packed_sfixed64/data-one.pbf differ
diff --git a/test/t/repeated_packed_sfixed64/test_cases.cpp b/test/t/repeated_packed_sfixed64/test_cases.cpp
new file mode 100644
index 0000000..08361b2
--- /dev/null
+++ b/test/t/repeated_packed_sfixed64/test_cases.cpp
@@ -0,0 +1,85 @@
+
+#include <test.hpp>
+
+TEST_CASE("read repeated packed sfixed64 field") {
+
+    SECTION("empty") {
+        std::string buffer = load_data("repeated_packed_sfixed64/data-empty");
+
+        protozero::pbf_reader item(buffer);
+
+        REQUIRE(!item.next());
+    }
+
+    SECTION("one") {
+        std::string buffer = load_data("repeated_packed_sfixed64/data-one");
+
+        protozero::pbf_reader item(buffer);
+
+        REQUIRE(item.next());
+        auto it_pair = item.get_packed_sfixed64();
+        REQUIRE(!item.next());
+
+        REQUIRE(*it_pair.first == 17LL);
+        REQUIRE(++it_pair.first == it_pair.second);
+    }
+
+    SECTION("many") {
+        std::string buffer = load_data("repeated_packed_sfixed64/data-many");
+
+        protozero::pbf_reader item(buffer);
+
+        REQUIRE(item.next());
+        auto it_pair = item.get_packed_sfixed64();
+        REQUIRE(!item.next());
+
+        auto it = it_pair.first;
+        REQUIRE(*it++ == 17LL);
+        REQUIRE(*it++ ==  0LL);
+        REQUIRE(*it++ ==  1LL);
+        REQUIRE(*it++ == -1LL);
+        REQUIRE(*it++ == std::numeric_limits<int64_t>::max());
+        REQUIRE(*it++ == std::numeric_limits<int64_t>::min());
+        REQUIRE(it == it_pair.second);
+    }
+
+    SECTION("end_of_buffer") {
+        std::string buffer = load_data("repeated_packed_sfixed64/data-many");
+
+        for (size_t i=1; i < buffer.size(); ++i) {
+            protozero::pbf_reader item(buffer.data(), i);
+            REQUIRE(item.next());
+            REQUIRE_THROWS_AS(item.get_packed_sfixed64(), protozero::end_of_buffer_exception);
+        }
+    }
+
+}
+
+TEST_CASE("write repeated packed sfixed64 field") {
+
+    std::string buffer;
+    protozero::pbf_writer pw(buffer);
+
+    SECTION("empty") {
+        int64_t data[] = { 17L };
+        pw.add_packed_sfixed64(1, std::begin(data), std::begin(data) /* !!!! */);
+
+        REQUIRE(buffer == load_data("repeated_packed_sfixed64/data-empty"));
+    }
+
+    SECTION("one") {
+        int64_t data[] = { 17L };
+        pw.add_packed_sfixed64(1, std::begin(data), std::end(data));
+
+        REQUIRE(buffer == load_data("repeated_packed_sfixed64/data-one"));
+    }
+
+    SECTION("many") {
+        int64_t data[] = { 17L, 0L, 1L, -1L, std::numeric_limits<int64_t>::max(), std::numeric_limits<int64_t>::min() };
+        pw.add_packed_sfixed64(1, std::begin(data), std::end(data));
+
+        REQUIRE(buffer == load_data("repeated_packed_sfixed64/data-many"));
+    }
+
+}
+
diff --git a/test/t/repeated_packed_sfixed64/testcase.cpp b/test/t/repeated_packed_sfixed64/testcase.cpp
new file mode 100644
index 0000000..90cafb9
--- /dev/null
+++ b/test/t/repeated_packed_sfixed64/testcase.cpp
@@ -0,0 +1,20 @@
+
+#include <testcase.hpp>
+#include "testcase.pb.h"
+
+int main(int c, char *argv[]) {
+    TestRepeatedPackedSFixed64::Test msg;
+
+    write_to_file(msg, "data-empty.pbf");
+
+    msg.add_i(17);
+    write_to_file(msg, "data-one.pbf");
+
+    msg.add_i(0);
+    msg.add_i(1);
+    msg.add_i(-1);
+    msg.add_i(std::numeric_limits<int64_t>::max());
+    msg.add_i(std::numeric_limits<int64_t>::min());
+    write_to_file(msg, "data-many.pbf");
+}
+
diff --git a/test/t/repeated_packed_sfixed64/testcase.proto b/test/t/repeated_packed_sfixed64/testcase.proto
new file mode 100644
index 0000000..fdd6538
--- /dev/null
+++ b/test/t/repeated_packed_sfixed64/testcase.proto
@@ -0,0 +1,11 @@
+
+option optimize_for = LITE_RUNTIME;
+
+package TestRepeatedPackedSFixed64;
+
+message Test {
+
+    repeated sfixed64 i = 1 [packed=true];
+
+}
+
diff --git a/test/t/repeated_packed_sint32/data-empty.pbf b/test/t/repeated_packed_sint32/data-empty.pbf
new file mode 100644
index 0000000..e69de29
diff --git a/test/t/repeated_packed_sint32/data-many.pbf b/test/t/repeated_packed_sint32/data-many.pbf
new file mode 100644
index 0000000..3e0bf6f
Binary files /dev/null and b/test/t/repeated_packed_sint32/data-many.pbf differ
diff --git a/test/t/repeated_packed_sint32/data-one.pbf b/test/t/repeated_packed_sint32/data-one.pbf
new file mode 100644
index 0000000..3c51bf9
--- /dev/null
+++ b/test/t/repeated_packed_sint32/data-one.pbf
@@ -0,0 +1,2 @@
+
+"
\ No newline at end of file
diff --git a/test/t/repeated_packed_sint32/test_cases.cpp b/test/t/repeated_packed_sint32/test_cases.cpp
new file mode 100644
index 0000000..23bbc41
--- /dev/null
+++ b/test/t/repeated_packed_sint32/test_cases.cpp
@@ -0,0 +1,85 @@
+
+#include <test.hpp>
+
+TEST_CASE("read repeated packed sint32 field") {
+
+    SECTION("empty") {
+        std::string buffer = load_data("repeated_packed_sint32/data-empty");
+
+        protozero::pbf_reader item(buffer);
+
+        REQUIRE(!item.next());
+    }
+
+    SECTION("one") {
+        std::string buffer = load_data("repeated_packed_sint32/data-one");
+
+        protozero::pbf_reader item(buffer);
+
+        REQUIRE(item.next());
+        auto it_pair = item.get_packed_sint32();
+        REQUIRE(!item.next());
+
+        REQUIRE(*it_pair.first == 17L);
+        REQUIRE(++it_pair.first == it_pair.second);
+    }
+
+    SECTION("many") {
+        std::string buffer = load_data("repeated_packed_sint32/data-many");
+
+        protozero::pbf_reader item(buffer);
+
+        REQUIRE(item.next());
+        auto it_pair = item.get_packed_sint32();
+        REQUIRE(!item.next());
+
+        auto it = it_pair.first;
+        REQUIRE(*it++ == 17L);
+        REQUIRE(*it++ ==  0L);
+        REQUIRE(*it++ ==  1L);
+        REQUIRE(*it++ == -1L);
+        REQUIRE(*it++ == std::numeric_limits<int32_t>::max());
+        REQUIRE(*it++ == std::numeric_limits<int32_t>::min());
+        REQUIRE(it == it_pair.second);
+    }
+
+    SECTION("end_of_buffer") {
+        std::string buffer = load_data("repeated_packed_sint32/data-many");
+
+        for (size_t i=1; i < buffer.size(); ++i) {
+            protozero::pbf_reader item(buffer.data(), i);
+            REQUIRE(item.next());
+            REQUIRE_THROWS_AS(item.get_packed_sint32(), protozero::end_of_buffer_exception);
+        }
+    }
+
+}
+
+TEST_CASE("write repeated packed sint32 field") {
+
+    std::string buffer;
+    protozero::pbf_writer pw(buffer);
+
+    SECTION("empty") {
+        int32_t data[] = { 17L };
+        pw.add_packed_sint32(1, std::begin(data), std::begin(data) /* !!!! */);
+
+        REQUIRE(buffer == load_data("repeated_packed_sint32/data-empty"));
+    }
+
+    SECTION("one") {
+        int32_t data[] = { 17L };
+        pw.add_packed_sint32(1, std::begin(data), std::end(data));
+
+        REQUIRE(buffer == load_data("repeated_packed_sint32/data-one"));
+    }
+
+    SECTION("many") {
+        int32_t data[] = { 17L, 0L, 1L, -1L, std::numeric_limits<int32_t>::max(), std::numeric_limits<int32_t>::min() };
+        pw.add_packed_sint32(1, std::begin(data), std::end(data));
+
+        REQUIRE(buffer == load_data("repeated_packed_sint32/data-many"));
+    }
+
+}
+
diff --git a/test/t/repeated_packed_sint32/testcase.cpp b/test/t/repeated_packed_sint32/testcase.cpp
new file mode 100644
index 0000000..3423f6d
--- /dev/null
+++ b/test/t/repeated_packed_sint32/testcase.cpp
@@ -0,0 +1,20 @@
+
+#include <testcase.hpp>
+#include "testcase.pb.h"
+
+int main(int c, char *argv[]) {
+    TestRepeatedPackedSInt32::Test msg;
+
+    write_to_file(msg, "data-empty.pbf");
+
+    msg.add_i(17L);
+    write_to_file(msg, "data-one.pbf");
+
+    msg.add_i(0L);
+    msg.add_i(1L);
+    msg.add_i(-1L);
+    msg.add_i(std::numeric_limits<int32_t>::max());
+    msg.add_i(std::numeric_limits<int32_t>::min());
+    write_to_file(msg, "data-many.pbf");
+}
+
diff --git a/test/t/repeated_packed_sint32/testcase.proto b/test/t/repeated_packed_sint32/testcase.proto
new file mode 100644
index 0000000..e1f48f7
--- /dev/null
+++ b/test/t/repeated_packed_sint32/testcase.proto
@@ -0,0 +1,11 @@
+
+option optimize_for = LITE_RUNTIME;
+
+package TestRepeatedPackedSInt32;
+
+message Test {
+
+    repeated sint32 i = 1 [packed=true];
+
+}
+
diff --git a/test/t/repeated_packed_sint64/data-empty.pbf b/test/t/repeated_packed_sint64/data-empty.pbf
new file mode 100644
index 0000000..e69de29
diff --git a/test/t/repeated_packed_sint64/data-many.pbf b/test/t/repeated_packed_sint64/data-many.pbf
new file mode 100644
index 0000000..b7eb8bf
Binary files /dev/null and b/test/t/repeated_packed_sint64/data-many.pbf differ
diff --git a/test/t/repeated_packed_sint64/data-one.pbf b/test/t/repeated_packed_sint64/data-one.pbf
new file mode 100644
index 0000000..3c51bf9
--- /dev/null
+++ b/test/t/repeated_packed_sint64/data-one.pbf
@@ -0,0 +1,2 @@
+
+"
\ No newline at end of file
diff --git a/test/t/repeated_packed_sint64/test_cases.cpp b/test/t/repeated_packed_sint64/test_cases.cpp
new file mode 100644
index 0000000..558f68d
--- /dev/null
+++ b/test/t/repeated_packed_sint64/test_cases.cpp
@@ -0,0 +1,85 @@
+
+#include <test.hpp>
+
+TEST_CASE("read repeated packed sint64 field") {
+
+    SECTION("empty") {
+        std::string buffer = load_data("repeated_packed_sint64/data-empty");
+
+        protozero::pbf_reader item(buffer);
+
+        REQUIRE(!item.next());
+    }
+
+    SECTION("one") {
+        std::string buffer = load_data("repeated_packed_sint64/data-one");
+
+        protozero::pbf_reader item(buffer);
+
+        REQUIRE(item.next());
+        auto it_pair = item.get_packed_sint64();
+        REQUIRE(!item.next());
+
+        REQUIRE(*it_pair.first == 17LL);
+        REQUIRE(++it_pair.first == it_pair.second);
+    }
+
+    SECTION("many") {
+        std::string buffer = load_data("repeated_packed_sint64/data-many");
+
+        protozero::pbf_reader item(buffer);
+
+        REQUIRE(item.next());
+        auto it_pair = item.get_packed_sint64();
+        REQUIRE(!item.next());
+
+        auto it = it_pair.first;
+        REQUIRE(*it++ == 17LL);
+        REQUIRE(*it++ ==  0LL);
+        REQUIRE(*it++ ==  1LL);
+        REQUIRE(*it++ == -1LL);
+        REQUIRE(*it++ == std::numeric_limits<int64_t>::max());
+        REQUIRE(*it++ == std::numeric_limits<int64_t>::min());
+        REQUIRE(it == it_pair.second);
+    }
+
+    SECTION("end_of_buffer") {
+        std::string buffer = load_data("repeated_packed_sint64/data-many");
+
+        for (size_t i=1; i < buffer.size(); ++i) {
+            protozero::pbf_reader item(buffer.data(), i);
+            REQUIRE(item.next());
+            REQUIRE_THROWS_AS(item.get_packed_sint64(), protozero::end_of_buffer_exception);
+        }
+    }
+
+}
+
+TEST_CASE("write repeated packed sint64 field") {
+
+    std::string buffer;
+    protozero::pbf_writer pw(buffer);
+
+    SECTION("empty") {
+        int64_t data[] = { 17L };
+        pw.add_packed_sint64(1, std::begin(data), std::begin(data) /* !!!! */);
+
+        REQUIRE(buffer == load_data("repeated_packed_sint64/data-empty"));
+    }
+
+    SECTION("one") {
+        int64_t data[] = { 17L };
+        pw.add_packed_sint64(1, std::begin(data), std::end(data));
+
+        REQUIRE(buffer == load_data("repeated_packed_sint64/data-one"));
+    }
+
+    SECTION("many") {
+        int64_t data[] = { 17L, 0L, 1L, -1L, std::numeric_limits<int64_t>::max(), std::numeric_limits<int64_t>::min() };
+        pw.add_packed_sint64(1, std::begin(data), std::end(data));
+
+        REQUIRE(buffer == load_data("repeated_packed_sint64/data-many"));
+    }
+
+}
+
diff --git a/test/t/repeated_packed_sint64/testcase.cpp b/test/t/repeated_packed_sint64/testcase.cpp
new file mode 100644
index 0000000..22d12e8
--- /dev/null
+++ b/test/t/repeated_packed_sint64/testcase.cpp
@@ -0,0 +1,20 @@
+
+#include <testcase.hpp>
+#include "testcase.pb.h"
+
+int main(int c, char *argv[]) {
+    TestRepeatedPackedSInt64::Test msg;
+
+    write_to_file(msg, "data-empty.pbf");
+
+    msg.add_i(17LL);
+    write_to_file(msg, "data-one.pbf");
+
+    msg.add_i(0LL);
+    msg.add_i(1LL);
+    msg.add_i(-1LL);
+    msg.add_i(std::numeric_limits<int64_t>::max());
+    msg.add_i(std::numeric_limits<int64_t>::min());
+    write_to_file(msg, "data-many.pbf");
+}
+
diff --git a/test/t/repeated_packed_sint64/testcase.proto b/test/t/repeated_packed_sint64/testcase.proto
new file mode 100644
index 0000000..160074f
--- /dev/null
+++ b/test/t/repeated_packed_sint64/testcase.proto
@@ -0,0 +1,11 @@
+
+option optimize_for = LITE_RUNTIME;
+
+package TestRepeatedPackedSInt64;
+
+message Test {
+
+    repeated sint64 i = 1 [packed=true];
+
+}
+
diff --git a/test/t/repeated_packed_uint32/data-empty.pbf b/test/t/repeated_packed_uint32/data-empty.pbf
new file mode 100644
index 0000000..e69de29
diff --git a/test/t/repeated_packed_uint32/data-many.pbf b/test/t/repeated_packed_uint32/data-many.pbf
new file mode 100644
index 0000000..47fb203
Binary files /dev/null and b/test/t/repeated_packed_uint32/data-many.pbf differ
diff --git a/test/t/repeated_packed_uint32/data-one.pbf b/test/t/repeated_packed_uint32/data-one.pbf
new file mode 100644
index 0000000..7f9c1a6
--- /dev/null
+++ b/test/t/repeated_packed_uint32/data-one.pbf
@@ -0,0 +1,2 @@
+
+
\ No newline at end of file
diff --git a/test/t/repeated_packed_uint32/test_cases.cpp b/test/t/repeated_packed_uint32/test_cases.cpp
new file mode 100644
index 0000000..a842402
--- /dev/null
+++ b/test/t/repeated_packed_uint32/test_cases.cpp
@@ -0,0 +1,83 @@
+
+#include <test.hpp>
+
+TEST_CASE("read repeated packed uint32 field") {
+
+    SECTION("empty") {
+        std::string buffer = load_data("repeated_packed_uint32/data-empty");
+
+        protozero::pbf_reader item(buffer);
+
+        REQUIRE(!item.next());
+    }
+
+    SECTION("one") {
+        std::string buffer = load_data("repeated_packed_uint32/data-one");
+
+        protozero::pbf_reader item(buffer);
+
+        REQUIRE(item.next());
+        auto it_pair = item.get_packed_uint32();
+        REQUIRE(!item.next());
+
+        REQUIRE(*it_pair.first == 17UL);
+        REQUIRE(++it_pair.first == it_pair.second);
+    }
+
+    SECTION("many") {
+        std::string buffer = load_data("repeated_packed_uint32/data-many");
+
+        protozero::pbf_reader item(buffer);
+
+        REQUIRE(item.next());
+        auto it_pair = item.get_packed_uint32();
+        REQUIRE(!item.next());
+
+        auto it = it_pair.first;
+        REQUIRE(*it++ == 17UL);
+        REQUIRE(*it++ ==  0UL);
+        REQUIRE(*it++ ==  1UL);
+        REQUIRE(*it++ == std::numeric_limits<uint32_t>::max());
+        REQUIRE(it == it_pair.second);
+    }
+
+    SECTION("end_of_buffer") {
+        std::string buffer = load_data("repeated_packed_uint32/data-many");
+
+        for (size_t i=1; i < buffer.size(); ++i) {
+            protozero::pbf_reader item(buffer.data(), i);
+            REQUIRE(item.next());
+            REQUIRE_THROWS_AS(item.get_packed_uint32(), protozero::end_of_buffer_exception);
+        }
+    }
+
+}
+
+TEST_CASE("write repeated packed uint32 field") {
+
+    std::string buffer;
+    protozero::pbf_writer pw(buffer);
+
+    SECTION("empty") {
+        uint32_t data[] = { 17UL };
+        pw.add_packed_uint32(1, std::begin(data), std::begin(data) /* !!!! */);
+
+        REQUIRE(buffer == load_data("repeated_packed_uint32/data-empty"));
+    }
+
+    SECTION("one") {
+        uint32_t data[] = { 17UL };
+        pw.add_packed_uint32(1, std::begin(data), std::end(data));
+
+        REQUIRE(buffer == load_data("repeated_packed_uint32/data-one"));
+    }
+
+    SECTION("many") {
+        uint32_t data[] = { 17UL, 0UL, 1UL, std::numeric_limits<uint32_t>::max() };
+        pw.add_packed_uint32(1, std::begin(data), std::end(data));
+
+        REQUIRE(buffer == load_data("repeated_packed_uint32/data-many"));
+    }
+
+}
+
diff --git a/test/t/repeated_packed_uint32/testcase.cpp b/test/t/repeated_packed_uint32/testcase.cpp
new file mode 100644
index 0000000..d228e5f
--- /dev/null
+++ b/test/t/repeated_packed_uint32/testcase.cpp
@@ -0,0 +1,18 @@
+
+#include <testcase.hpp>
+#include "testcase.pb.h"
+
+int main(int c, char *argv[]) {
+    TestRepeatedPackedUInt32::Test msg;
+
+    write_to_file(msg, "data-empty.pbf");
+
+    msg.add_i(17UL);
+    write_to_file(msg, "data-one.pbf");
+
+    msg.add_i(0UL);
+    msg.add_i(1UL);
+    msg.add_i(std::numeric_limits<uint32_t>::max());
+    write_to_file(msg, "data-many.pbf");
+}
+
diff --git a/test/t/repeated_packed_uint32/testcase.proto b/test/t/repeated_packed_uint32/testcase.proto
new file mode 100644
index 0000000..8d1647b
--- /dev/null
+++ b/test/t/repeated_packed_uint32/testcase.proto
@@ -0,0 +1,11 @@
+
+option optimize_for = LITE_RUNTIME;
+
+package TestRepeatedPackedUInt32;
+
+message Test {
+
+    repeated uint32 i = 1 [packed=true];
+
+}
+
diff --git a/test/t/repeated_packed_uint64/data-empty.pbf b/test/t/repeated_packed_uint64/data-empty.pbf
new file mode 100644
index 0000000..e69de29
diff --git a/test/t/repeated_packed_uint64/data-many.pbf b/test/t/repeated_packed_uint64/data-many.pbf
new file mode 100644
index 0000000..bbdfbcf
Binary files /dev/null and b/test/t/repeated_packed_uint64/data-many.pbf differ
diff --git a/test/t/repeated_packed_uint64/data-one.pbf b/test/t/repeated_packed_uint64/data-one.pbf
new file mode 100644
index 0000000..7f9c1a6
--- /dev/null
+++ b/test/t/repeated_packed_uint64/data-one.pbf
@@ -0,0 +1,2 @@
+
+
\ No newline at end of file
diff --git a/test/t/repeated_packed_uint64/test_cases.cpp b/test/t/repeated_packed_uint64/test_cases.cpp
new file mode 100644
index 0000000..2fdf1c4
--- /dev/null
+++ b/test/t/repeated_packed_uint64/test_cases.cpp
@@ -0,0 +1,83 @@
+
+#include <test.hpp>
+
+TEST_CASE("read repeated packed uint64 field") {
+
+    SECTION("empty") {
+        std::string buffer = load_data("repeated_packed_uint64/data-empty");
+
+        protozero::pbf_reader item(buffer);
+
+        REQUIRE(!item.next());
+    }
+
+    SECTION("one") {
+        std::string buffer = load_data("repeated_packed_uint64/data-one");
+
+        protozero::pbf_reader item(buffer);
+
+        REQUIRE(item.next());
+        auto it_pair = item.get_packed_uint64();
+        REQUIRE(!item.next());
+
+        REQUIRE(*it_pair.first == 17ULL);
+        REQUIRE(++it_pair.first == it_pair.second);
+    }
+
+    SECTION("many") {
+        std::string buffer = load_data("repeated_packed_uint64/data-many");
+
+        protozero::pbf_reader item(buffer);
+
+        REQUIRE(item.next());
+        auto it_pair = item.get_packed_uint64();
+        REQUIRE(!item.next());
+
+        auto it = it_pair.first;
+        REQUIRE(*it++ == 17ULL);
+        REQUIRE(*it++ ==  0ULL);
+        REQUIRE(*it++ ==  1ULL);
+        REQUIRE(*it++ == std::numeric_limits<uint64_t>::max());
+        REQUIRE(it == it_pair.second);
+    }
+
+    SECTION("end_of_buffer") {
+        std::string buffer = load_data("repeated_packed_uint64/data-many");
+
+        for (size_t i=1; i < buffer.size(); ++i) {
+            protozero::pbf_reader item(buffer.data(), i);
+            REQUIRE(item.next());
+            REQUIRE_THROWS_AS(item.get_packed_uint64(), protozero::end_of_buffer_exception);
+        }
+    }
+
+}
+
+TEST_CASE("write repeated packed uint64 field") {
+
+    std::string buffer;
+    protozero::pbf_writer pw(buffer);
+
+    SECTION("empty") {
+        uint64_t data[] = { 17UL };
+        pw.add_packed_uint64(1, std::begin(data), std::begin(data) /* !!!! */);
+
+        REQUIRE(buffer == load_data("repeated_packed_uint64/data-empty"));
+    }
+
+    SECTION("one") {
+        uint64_t data[] = { 17UL };
+        pw.add_packed_uint64(1, std::begin(data), std::end(data));
+
+        REQUIRE(buffer == load_data("repeated_packed_uint64/data-one"));
+    }
+
+    SECTION("many") {
+        uint64_t data[] = { 17UL, 0UL, 1UL, std::numeric_limits<uint64_t>::max() };
+        pw.add_packed_uint64(1, std::begin(data), std::end(data));
+
+        REQUIRE(buffer == load_data("repeated_packed_uint64/data-many"));
+    }
+
+}
+
diff --git a/test/t/repeated_packed_uint64/testcase.cpp b/test/t/repeated_packed_uint64/testcase.cpp
new file mode 100644
index 0000000..ffefe09
--- /dev/null
+++ b/test/t/repeated_packed_uint64/testcase.cpp
@@ -0,0 +1,18 @@
+
+#include <testcase.hpp>
+#include "testcase.pb.h"
+
+int main(int c, char *argv[]) {
+    TestRepeatedPackedUInt64::Test msg;
+
+    write_to_file(msg, "data-empty.pbf");
+
+    msg.add_i(17ULL);
+    write_to_file(msg, "data-one.pbf");
+
+    msg.add_i(0ULL);
+    msg.add_i(1ULL);
+    msg.add_i(std::numeric_limits<uint64_t>::max());
+    write_to_file(msg, "data-many.pbf");
+}
+
diff --git a/test/t/repeated_packed_uint64/testcase.proto b/test/t/repeated_packed_uint64/testcase.proto
new file mode 100644
index 0000000..fbbc57a
--- /dev/null
+++ b/test/t/repeated_packed_uint64/testcase.proto
@@ -0,0 +1,11 @@
+
+option optimize_for = LITE_RUNTIME;
+
+package TestRepeatedPackedUInt64;
+
+message Test {
+
+    repeated uint64 i = 1 [packed=true];
+
+}
+
diff --git a/test/t/sfixed32/data-max-int.pbf b/test/t/sfixed32/data-max-int.pbf
new file mode 100644
index 0000000..52fe7ac
--- /dev/null
+++ b/test/t/sfixed32/data-max-int.pbf
@@ -0,0 +1 @@
+
���
\ No newline at end of file
diff --git a/test/t/sfixed32/data-min-int.pbf b/test/t/sfixed32/data-min-int.pbf
new file mode 100644
index 0000000..2c570d4
Binary files /dev/null and b/test/t/sfixed32/data-min-int.pbf differ
diff --git a/test/t/sfixed32/data-zero.pbf b/test/t/sfixed32/data-zero.pbf
new file mode 100644
index 0000000..1cf2c40
Binary files /dev/null and b/test/t/sfixed32/data-zero.pbf differ
diff --git a/test/t/sfixed32/test_cases.cpp b/test/t/sfixed32/test_cases.cpp
new file mode 100644
index 0000000..70e251c
--- /dev/null
+++ b/test/t/sfixed32/test_cases.cpp
@@ -0,0 +1,69 @@
+
+#include <test.hpp>
+
+TEST_CASE("read sfixed32 field") {
+
+    SECTION("zero") {
+        std::string buffer = load_data("sfixed32/data-zero");
+
+        protozero::pbf_reader item(buffer);
+
+        REQUIRE(item.next());
+        REQUIRE(item.get_sfixed32() == 0L);
+        REQUIRE(!item.next());
+    }
+
+    SECTION("max-int") {
+        std::string buffer = load_data("sfixed32/data-max-int");
+
+        protozero::pbf_reader item(buffer);
+
+        REQUIRE(item.next());
+        REQUIRE(item.get_sfixed32() == std::numeric_limits<int32_t>::max());
+        REQUIRE(!item.next());
+    }
+
+    SECTION("min-int") {
+        std::string buffer = load_data("sfixed32/data-min-int");
+
+        protozero::pbf_reader item(buffer);
+
+        REQUIRE(item.next());
+        REQUIRE(item.get_sfixed32() == std::numeric_limits<int32_t>::min());
+        REQUIRE(!item.next());
+    }
+
+    SECTION("end_of_buffer") {
+        std::string buffer = load_data("sfixed32/data-min-int");
+
+        for (size_t i=1; i < buffer.size(); ++i) {
+            protozero::pbf_reader item(buffer.data(), i);
+            REQUIRE(item.next());
+            REQUIRE_THROWS_AS(item.get_sfixed32(), protozero::end_of_buffer_exception);
+        }
+    }
+
+}
+
+TEST_CASE("write sfixed32 field") {
+
+    std::string buffer;
+    protozero::pbf_writer pw(buffer);
+
+    SECTION("zero") {
+        pw.add_sfixed32(1, 0);
+        REQUIRE(buffer == load_data("sfixed32/data-zero"));
+    }
+
+    SECTION("max-uint") {
+        pw.add_sfixed32(1, std::numeric_limits<int32_t>::max());
+        REQUIRE(buffer == load_data("sfixed32/data-max-int"));
+    }
+
+    SECTION("min-uint") {
+        pw.add_sfixed32(1, std::numeric_limits<int32_t>::min());
+        REQUIRE(buffer == load_data("sfixed32/data-min-int"));
+    }
+
+}
+
diff --git a/test/t/sfixed32/testcase.cpp b/test/t/sfixed32/testcase.cpp
new file mode 100644
index 0000000..a50bd40
--- /dev/null
+++ b/test/t/sfixed32/testcase.cpp
@@ -0,0 +1,17 @@
+
+#include <testcase.hpp>
+#include "testcase.pb.h"
+
+int main(int c, char *argv[]) {
+    TestSFixed32::Test msg;
+
+    msg.set_i(0);
+    write_to_file(msg, "data-zero.pbf");
+
+    msg.set_i(std::numeric_limits<int32_t>::max());
+    write_to_file(msg, "data-max-int.pbf");
+
+    msg.set_i(std::numeric_limits<int32_t>::min());
+    write_to_file(msg, "data-min-int.pbf");
+}
+
diff --git a/test/t/sfixed32/testcase.proto b/test/t/sfixed32/testcase.proto
new file mode 100644
index 0000000..c2cb3f1
--- /dev/null
+++ b/test/t/sfixed32/testcase.proto
@@ -0,0 +1,11 @@
+
+option optimize_for = LITE_RUNTIME;
+
+package TestSFixed32;
+
+message Test {
+
+    required sfixed32 i = 1;
+
+}
+
diff --git a/test/t/sfixed64/data-max-int.pbf b/test/t/sfixed64/data-max-int.pbf
new file mode 100644
index 0000000..7ec5cac
--- /dev/null
+++ b/test/t/sfixed64/data-max-int.pbf
@@ -0,0 +1 @@
+	�������
\ No newline at end of file
diff --git a/test/t/sfixed64/data-min-int.pbf b/test/t/sfixed64/data-min-int.pbf
new file mode 100644
index 0000000..827eb05
Binary files /dev/null and b/test/t/sfixed64/data-min-int.pbf differ
diff --git a/test/t/sfixed64/data-zero.pbf b/test/t/sfixed64/data-zero.pbf
new file mode 100644
index 0000000..5f47e98
Binary files /dev/null and b/test/t/sfixed64/data-zero.pbf differ
diff --git a/test/t/sfixed64/test_cases.cpp b/test/t/sfixed64/test_cases.cpp
new file mode 100644
index 0000000..ae8ca60
--- /dev/null
+++ b/test/t/sfixed64/test_cases.cpp
@@ -0,0 +1,69 @@
+
+#include <test.hpp>
+
+TEST_CASE("read sfixed64 field") {
+
+    SECTION("zero") {
+        std::string buffer = load_data("sfixed64/data-zero");
+
+        protozero::pbf_reader item(buffer);
+
+        REQUIRE(item.next());
+        REQUIRE(item.get_sfixed64() == 0LL);
+        REQUIRE(!item.next());
+    }
+
+    SECTION("max-int") {
+        std::string buffer = load_data("sfixed64/data-max-int");
+
+        protozero::pbf_reader item(buffer);
+
+        REQUIRE(item.next());
+        REQUIRE(item.get_sfixed64() == std::numeric_limits<int64_t>::max());
+        REQUIRE(!item.next());
+    }
+
+    SECTION("min-int") {
+        std::string buffer = load_data("sfixed64/data-min-int");
+
+        protozero::pbf_reader item(buffer);
+
+        REQUIRE(item.next());
+        REQUIRE(item.get_sfixed64() == std::numeric_limits<int64_t>::min());
+        REQUIRE(!item.next());
+    }
+
+    SECTION("end_of_buffer") {
+        std::string buffer = load_data("sfixed64/data-min-int");
+
+        for (size_t i=1; i < buffer.size(); ++i) {
+            protozero::pbf_reader item(buffer.data(), i);
+            REQUIRE(item.next());
+            REQUIRE_THROWS_AS(item.get_sfixed64(), protozero::end_of_buffer_exception);
+        }
+    }
+
+}
+
+TEST_CASE("write sfixed64 field") {
+
+    std::string buffer;
+    protozero::pbf_writer pw(buffer);
+
+    SECTION("zero") {
+        pw.add_sfixed64(1, 0);
+        REQUIRE(buffer == load_data("sfixed64/data-zero"));
+    }
+
+    SECTION("max-uint") {
+        pw.add_sfixed64(1, std::numeric_limits<int64_t>::max());
+        REQUIRE(buffer == load_data("sfixed64/data-max-int"));
+    }
+
+    SECTION("min-uint") {
+        pw.add_sfixed64(1, std::numeric_limits<int64_t>::min());
+        REQUIRE(buffer == load_data("sfixed64/data-min-int"));
+    }
+
+}
+
diff --git a/test/t/sfixed64/testcase.cpp b/test/t/sfixed64/testcase.cpp
new file mode 100644
index 0000000..3ce45be
--- /dev/null
+++ b/test/t/sfixed64/testcase.cpp
@@ -0,0 +1,17 @@
+
+#include <testcase.hpp>
+#include "testcase.pb.h"
+
+int main(int c, char *argv[]) {
+    TestSFixed64::Test msg;
+
+    msg.set_i(0);
+    write_to_file(msg, "data-zero.pbf");
+
+    msg.set_i(std::numeric_limits<int64_t>::max());
+    write_to_file(msg, "data-max-int.pbf");
+
+    msg.set_i(std::numeric_limits<int64_t>::min());
+    write_to_file(msg, "data-min-int.pbf");
+}
+
diff --git a/test/t/sfixed64/testcase.proto b/test/t/sfixed64/testcase.proto
new file mode 100644
index 0000000..2c58753
--- /dev/null
+++ b/test/t/sfixed64/testcase.proto
@@ -0,0 +1,11 @@
+
+option optimize_for = LITE_RUNTIME;
+
+package TestSFixed64;
+
+message Test {
+
+    required sfixed64 i = 1;
+
+}
+
diff --git a/test/t/sint32/data-max.pbf b/test/t/sint32/data-max.pbf
new file mode 100644
index 0000000..dfd6e7f
--- /dev/null
+++ b/test/t/sint32/data-max.pbf
@@ -0,0 +1 @@
+����
\ No newline at end of file
diff --git a/test/t/sint32/data-min.pbf b/test/t/sint32/data-min.pbf
new file mode 100644
index 0000000..909ea9a
--- /dev/null
+++ b/test/t/sint32/data-min.pbf
@@ -0,0 +1 @@
+����
\ No newline at end of file
diff --git a/test/t/sint32/data-neg.pbf b/test/t/sint32/data-neg.pbf
new file mode 100644
index 0000000..e19a122
--- /dev/null
+++ b/test/t/sint32/data-neg.pbf
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/test/t/sint32/data-pos.pbf b/test/t/sint32/data-pos.pbf
new file mode 100644
index 0000000..009a24f
--- /dev/null
+++ b/test/t/sint32/data-pos.pbf
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/test/t/sint32/data-zero.pbf b/test/t/sint32/data-zero.pbf
new file mode 100644
index 0000000..268720a
Binary files /dev/null and b/test/t/sint32/data-zero.pbf differ
diff --git a/test/t/sint32/test_cases.cpp b/test/t/sint32/test_cases.cpp
new file mode 100644
index 0000000..b75e1dc
--- /dev/null
+++ b/test/t/sint32/test_cases.cpp
@@ -0,0 +1,99 @@
+
+#include <test.hpp>
+
+TEST_CASE("read sint32 field") {
+
+    SECTION("zero") {
+        std::string buffer = load_data("sint32/data-zero");
+
+        protozero::pbf_reader item(buffer);
+
+        REQUIRE(item.next());
+        REQUIRE(item.get_sint32() == 0L);
+        REQUIRE(!item.next());
+    }
+
+    SECTION("positive") {
+        std::string buffer = load_data("sint32/data-pos");
+
+        protozero::pbf_reader item(buffer);
+
+        REQUIRE(item.next());
+        REQUIRE(item.get_sint32() == 1L);
+        REQUIRE(!item.next());
+    }
+
+    SECTION("negative") {
+        std::string buffer = load_data("sint32/data-neg");
+
+        protozero::pbf_reader item(buffer);
+
+        REQUIRE(item.next());
+        REQUIRE(item.get_sint32() == -1L);
+        REQUIRE(!item.next());
+    }
+
+    SECTION("max") {
+        std::string buffer = load_data("sint32/data-max");
+
+        protozero::pbf_reader item(buffer);
+
+        REQUIRE(item.next());
+        REQUIRE(item.get_sint32() == std::numeric_limits<int32_t>::max());
+        REQUIRE(!item.next());
+    }
+
+    SECTION("min") {
+        std::string buffer = load_data("sint32/data-min");
+
+        protozero::pbf_reader item(buffer);
+
+        REQUIRE(item.next());
+        REQUIRE(item.get_sint32() == std::numeric_limits<int32_t>::min());
+        REQUIRE(!item.next());
+    }
+
+    SECTION("end_of_buffer") {
+        std::string buffer = load_data("sint32/data-min");
+
+        for (size_t i=1; i < buffer.size(); ++i) {
+            protozero::pbf_reader item(buffer.data(), i);
+            REQUIRE(item.next());
+            REQUIRE_THROWS_AS(item.get_sint32(), protozero::end_of_buffer_exception);
+        }
+    }
+
+}
+
+TEST_CASE("write sint32 field") {
+
+    std::string buffer;
+    protozero::pbf_writer pw(buffer);
+
+    SECTION("zero") {
+        pw.add_sint32(1, 0L);
+        REQUIRE(buffer == load_data("sint32/data-zero"));
+    }
+
+    SECTION("positive") {
+        pw.add_sint32(1, 1L);
+        REQUIRE(buffer == load_data("sint32/data-pos"));
+    }
+
+    SECTION("negative") {
+        pw.add_sint32(1, -1L);
+        REQUIRE(buffer == load_data("sint32/data-neg"));
+    }
+
+    SECTION("max") {
+        pw.add_sint32(1, std::numeric_limits<int32_t>::max());
+        REQUIRE(buffer == load_data("sint32/data-max"));
+    }
+
+    SECTION("min") {
+        pw.add_sint32(1, std::numeric_limits<int32_t>::min());
+        REQUIRE(buffer == load_data("sint32/data-min"));
+    }
+
+}
+
diff --git a/test/t/sint32/testcase.cpp b/test/t/sint32/testcase.cpp
new file mode 100644
index 0000000..cbd7fc1
--- /dev/null
+++ b/test/t/sint32/testcase.cpp
@@ -0,0 +1,23 @@
+
+#include <testcase.hpp>
+#include "testcase.pb.h"
+
+int main(int c, char *argv[]) {
+    TestSInt32::Test msg;
+
+    msg.set_i(0L);
+    write_to_file(msg, "data-zero.pbf");
+
+    msg.set_i(1L);
+    write_to_file(msg, "data-pos.pbf");
+
+    msg.set_i(-1L);
+    write_to_file(msg, "data-neg.pbf");
+
+    msg.set_i(std::numeric_limits<int32_t>::max());
+    write_to_file(msg, "data-max.pbf");
+
+    msg.set_i(std::numeric_limits<int32_t>::min());
+    write_to_file(msg, "data-min.pbf");
+}
+
diff --git a/test/t/sint32/testcase.proto b/test/t/sint32/testcase.proto
new file mode 100644
index 0000000..d5f1ab9
--- /dev/null
+++ b/test/t/sint32/testcase.proto
@@ -0,0 +1,11 @@
+
+option optimize_for = LITE_RUNTIME;
+
+package TestSInt32;
+
+message Test {
+
+    required sint32 i = 1;
+
+}
+
diff --git a/test/t/sint64/data-max.pbf b/test/t/sint64/data-max.pbf
new file mode 100644
index 0000000..7b57742
--- /dev/null
+++ b/test/t/sint64/data-max.pbf
@@ -0,0 +1 @@
+���������
\ No newline at end of file
diff --git a/test/t/sint64/data-min.pbf b/test/t/sint64/data-min.pbf
new file mode 100644
index 0000000..1c2f041
--- /dev/null
+++ b/test/t/sint64/data-min.pbf
@@ -0,0 +1 @@
+���������
\ No newline at end of file
diff --git a/test/t/sint64/data-neg.pbf b/test/t/sint64/data-neg.pbf
new file mode 100644
index 0000000..e19a122
--- /dev/null
+++ b/test/t/sint64/data-neg.pbf
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/test/t/sint64/data-pos.pbf b/test/t/sint64/data-pos.pbf
new file mode 100644
index 0000000..009a24f
--- /dev/null
+++ b/test/t/sint64/data-pos.pbf
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/test/t/sint64/data-zero.pbf b/test/t/sint64/data-zero.pbf
new file mode 100644
index 0000000..268720a
Binary files /dev/null and b/test/t/sint64/data-zero.pbf differ
diff --git a/test/t/sint64/test_cases.cpp b/test/t/sint64/test_cases.cpp
new file mode 100644
index 0000000..8f6eaf4
--- /dev/null
+++ b/test/t/sint64/test_cases.cpp
@@ -0,0 +1,99 @@
+
+#include <test.hpp>
+
+TEST_CASE("read sint64 field") {
+
+    SECTION("zero") {
+        std::string buffer = load_data("sint64/data-zero");
+
+        protozero::pbf_reader item(buffer);
+
+        REQUIRE(item.next());
+        REQUIRE(item.get_sint64() == 0LL);
+        REQUIRE(!item.next());
+    }
+
+    SECTION("positive") {
+        std::string buffer = load_data("sint64/data-pos");
+
+        protozero::pbf_reader item(buffer);
+
+        REQUIRE(item.next());
+        REQUIRE(item.get_sint64() == 1LL);
+        REQUIRE(!item.next());
+    }
+
+    SECTION("negative") {
+        std::string buffer = load_data("sint64/data-neg");
+
+        protozero::pbf_reader item(buffer);
+
+        REQUIRE(item.next());
+        REQUIRE(item.get_sint64() == -1LL);
+        REQUIRE(!item.next());
+    }
+
+    SECTION("max") {
+        std::string buffer = load_data("sint64/data-max");
+
+        protozero::pbf_reader item(buffer);
+
+        REQUIRE(item.next());
+        REQUIRE(item.get_sint64() == std::numeric_limits<int64_t>::max());
+        REQUIRE(!item.next());
+    }
+
+    SECTION("min") {
+        std::string buffer = load_data("sint64/data-min");
+
+        protozero::pbf_reader item(buffer);
+
+        REQUIRE(item.next());
+        REQUIRE(item.get_sint64() == std::numeric_limits<int64_t>::min());
+        REQUIRE(!item.next());
+    }
+
+    SECTION("end_of_buffer") {
+        std::string buffer = load_data("sint64/data-min");
+
+        for (size_t i=1; i < buffer.size(); ++i) {
+            protozero::pbf_reader item(buffer.data(), i);
+            REQUIRE(item.next());
+            REQUIRE_THROWS_AS(item.get_sint64(), protozero::end_of_buffer_exception);
+        }
+    }
+
+}
+
+TEST_CASE("write sint64 field") {
+
+    std::string buffer;
+    protozero::pbf_writer pw(buffer);
+
+    SECTION("zero") {
+        pw.add_sint64(1, 0L);
+        REQUIRE(buffer == load_data("sint64/data-zero"));
+    }
+
+    SECTION("positive") {
+        pw.add_sint64(1, 1L);
+        REQUIRE(buffer == load_data("sint64/data-pos"));
+    }
+
+    SECTION("negative") {
+        pw.add_sint64(1, -1L);
+        REQUIRE(buffer == load_data("sint64/data-neg"));
+    }
+
+    SECTION("max") {
+        pw.add_sint64(1, std::numeric_limits<int64_t>::max());
+        REQUIRE(buffer == load_data("sint64/data-max"));
+    }
+
+    SECTION("min") {
+        pw.add_sint64(1, std::numeric_limits<int64_t>::min());
+        REQUIRE(buffer == load_data("sint64/data-min"));
+    }
+
+}
+
diff --git a/test/t/sint64/testcase.cpp b/test/t/sint64/testcase.cpp
new file mode 100644
index 0000000..051afd6
--- /dev/null
+++ b/test/t/sint64/testcase.cpp
@@ -0,0 +1,23 @@
+
+#include <testcase.hpp>
+#include "testcase.pb.h"
+
+int main(int c, char *argv[]) {
+    TestSInt64::Test msg;
+
+    msg.set_i(0LL);
+    write_to_file(msg, "data-zero.pbf");
+
+    msg.set_i(1LL);
+    write_to_file(msg, "data-pos.pbf");
+
+    msg.set_i(-1LL);
+    write_to_file(msg, "data-neg.pbf");
+
+    msg.set_i(std::numeric_limits<int64_t>::max());
+    write_to_file(msg, "data-max.pbf");
+
+    msg.set_i(std::numeric_limits<int64_t>::min());
+    write_to_file(msg, "data-min.pbf");
+}
+
diff --git a/test/t/sint64/testcase.proto b/test/t/sint64/testcase.proto
new file mode 100644
index 0000000..bf31324
--- /dev/null
+++ b/test/t/sint64/testcase.proto
@@ -0,0 +1,11 @@
+
+option optimize_for = LITE_RUNTIME;
+
+package TestSInt64;
+
+message Test {
+
+    required sint64 i = 1;
+
+}
+
diff --git a/test/t/skip/test_cases.cpp b/test/t/skip/test_cases.cpp
new file mode 100644
index 0000000..70f932f
--- /dev/null
+++ b/test/t/skip/test_cases.cpp
@@ -0,0 +1,141 @@
+
+#include <test.hpp>
+
+TEST_CASE("skip") {
+
+    SECTION("check that skip() skips the right amount of bytes") {
+
+        // These are all the data files which contain exactly one field.
+        //
+        // Create this list with:
+        //   cd test/t
+        //   find . -name data\*pbf -not -empty | sed -e 's/^.\/\(.*\).pbf/"\1",/'
+        // and then remove everything manually that contains more than one
+        // field.
+        std::vector<std::string> filenames = {
+            "bool/data-also-true",
+            "bool/data-false",
+            "bool/data-still-true",
+            "bool/data-true",
+            "bytes/data-binary",
+            "bytes/data-empty",
+            "bytes/data-one",
+            "bytes/data-string",
+            "double/data-neg",
+            "double/data-pos",
+            "double/data-zero",
+            "enum/data-black",
+            "enum/data-blue",
+            "fixed32/data-max-uint",
+            "fixed32/data-min-uint",
+            "fixed32/data-zero",
+            "fixed64/data-max-uint",
+            "fixed64/data-min-uint",
+            "fixed64/data-zero",
+            "float/data-neg",
+            "float/data-pos",
+            "float/data-zero",
+            "int32/data-max",
+            "int32/data-min",
+            "int32/data-neg",
+            "int32/data-pos",
+            "int32/data-zero",
+            "int64/data-max",
+            "int64/data-min",
+            "int64/data-neg",
+            "int64/data-pos",
+            "int64/data-zero",
+            "message/data-message",
+            "repeated/data-one",
+            "repeated_packed_fixed32/data-many",
+            "repeated_packed_fixed32/data-one",
+            "repeated_packed_fixed64/data-many",
+            "repeated_packed_fixed64/data-one",
+            "repeated_packed_int32/data-many",
+            "repeated_packed_int32/data-one",
+            "repeated_packed_int64/data-many",
+            "repeated_packed_int64/data-one",
+            "repeated_packed_sfixed32/data-many",
+            "repeated_packed_sfixed32/data-one",
+            "repeated_packed_sfixed64/data-many",
+            "repeated_packed_sfixed64/data-one",
+            "repeated_packed_sint32/data-many",
+            "repeated_packed_sint32/data-one",
+            "repeated_packed_sint64/data-many",
+            "repeated_packed_sint64/data-one",
+            "repeated_packed_uint32/data-many",
+            "repeated_packed_uint32/data-one",
+            "repeated_packed_uint64/data-many",
+            "repeated_packed_uint64/data-one",
+            "sfixed32/data-max-int",
+            "sfixed32/data-min-int",
+            "sfixed32/data-zero",
+            "sfixed64/data-max-int",
+            "sfixed64/data-min-int",
+            "sfixed64/data-zero",
+            "sint32/data-max",
+            "sint32/data-min",
+            "sint32/data-neg",
+            "sint32/data-pos",
+            "sint32/data-zero",
+            "sint64/data-max",
+            "sint64/data-min",
+            "sint64/data-neg",
+            "sint64/data-pos",
+            "sint64/data-zero",
+            "string/data-empty",
+            "string/data-one",
+            "string/data-string",
+            "tags/data-tag-1",
+            "tags/data-tag-200000",
+            "tags/data-tag-200",
+            "tags/data-tag-max",
+            "uint32/data-max",
+            "uint32/data-pos",
+            "uint32/data-zero",
+            "uint64/data-max",
+            "uint64/data-pos",
+            "uint64/data-zero",
+        };
+
+        for (const auto& filename : filenames) {
+            std::string buffer = load_data(filename);
+
+            protozero::pbf_reader item(buffer);
+
+            REQUIRE(item.next());
+            item.skip();
+            REQUIRE(!item);
+        }
+
+    }
+
+    SECTION("check that skip() throws on unknown field type") {
+        std::string buffer;
+
+        protozero::pbf_writer pw(buffer);
+        pw.add_fixed32(1, 123);
+
+        buffer[0] += 1; // hack to create illegal field type
+
+        protozero::pbf_reader item(buffer);
+
+        REQUIRE(item.next());
+        REQUIRE_THROWS_AS(item.skip(), protozero::unknown_pbf_wire_type_exception);
+    }
+
+    SECTION("check that skip() throws on short buffer") {
+        std::string buffer;
+
+        protozero::pbf_writer pw(buffer);
+        pw.add_fixed32(1, 123);
+
+        buffer.resize(buffer.size() - 1); // "remove" last byte from buffer
+        protozero::pbf_reader item(buffer);
+
+        REQUIRE(item.next());
+        REQUIRE_THROWS_AS(item.skip(), protozero::end_of_buffer_exception);
+    }
+
+}
+
diff --git a/test/t/string/data-empty.pbf b/test/t/string/data-empty.pbf
new file mode 100644
index 0000000..59f144e
Binary files /dev/null and b/test/t/string/data-empty.pbf differ
diff --git a/test/t/string/data-one.pbf b/test/t/string/data-one.pbf
new file mode 100644
index 0000000..2764cf5
--- /dev/null
+++ b/test/t/string/data-one.pbf
@@ -0,0 +1,2 @@
+
+x
\ No newline at end of file
diff --git a/test/t/string/data-string.pbf b/test/t/string/data-string.pbf
new file mode 100644
index 0000000..d7369a7
--- /dev/null
+++ b/test/t/string/data-string.pbf
@@ -0,0 +1,2 @@
+
+foobar
\ No newline at end of file
diff --git a/test/t/string/test_cases.cpp b/test/t/string/test_cases.cpp
new file mode 100644
index 0000000..5d840ff
--- /dev/null
+++ b/test/t/string/test_cases.cpp
@@ -0,0 +1,78 @@
+
+#include <test.hpp>
+
+TEST_CASE("read string field") {
+
+    SECTION("empty") {
+        std::string buffer = load_data("string/data-empty");
+
+        protozero::pbf_reader item(buffer);
+
+        REQUIRE(item.next());
+        REQUIRE(item.get_string() == "");
+        REQUIRE(!item.next());
+    }
+
+    SECTION("one") {
+        std::string buffer = load_data("string/data-one");
+
+        protozero::pbf_reader item(buffer);
+
+        REQUIRE(item.next());
+        REQUIRE(item.get_string() == "x");
+        REQUIRE(!item.next());
+    }
+
+    SECTION("string") {
+        std::string buffer = load_data("string/data-string");
+
+        protozero::pbf_reader item(buffer);
+
+        REQUIRE(item.next());
+        REQUIRE(item.get_string() == "foobar");
+        REQUIRE(!item.next());
+    }
+
+    SECTION("end_of_buffer") {
+        std::string buffer = load_data("string/data-string");
+
+        for (size_t i=1; i < buffer.size(); ++i) {
+            protozero::pbf_reader item(buffer.data(), i);
+            REQUIRE(item.next());
+            REQUIRE_THROWS_AS(item.get_string(), protozero::end_of_buffer_exception);
+        }
+    }
+
+    SECTION("check assert on fixed int access") {
+        std::string buffer = load_data("string/data-string");
+
+        protozero::pbf_reader item(buffer);
+
+        REQUIRE(item.next());
+        REQUIRE_THROWS_AS(item.get_fixed32(), assert_error);
+    }
+
+}
+
+TEST_CASE("write string field") {
+
+    std::string buffer_test;
+    protozero::pbf_writer pbf_test(buffer_test);
+
+    SECTION("empty") {
+        pbf_test.add_string(1, "");
+        REQUIRE(buffer_test == load_data("string/data-empty"));
+    }
+
+    SECTION("one") {
+        pbf_test.add_string(1, "x");
+        REQUIRE(buffer_test == load_data("string/data-one"));
+    }
+
+    SECTION("string") {
+        pbf_test.add_string(1, "foobar");
+        REQUIRE(buffer_test == load_data("string/data-string"));
+    }
+
+}
+
diff --git a/test/t/string/testcase.cpp b/test/t/string/testcase.cpp
new file mode 100644
index 0000000..28cb7a0
--- /dev/null
+++ b/test/t/string/testcase.cpp
@@ -0,0 +1,17 @@
+
+#include <testcase.hpp>
+#include "testcase.pb.h"
+
+int main(int c, char *argv[]) {
+    TestString::Test msg;
+
+    msg.set_s("");
+    write_to_file(msg, "data-empty.pbf");
+
+    msg.set_s("x");
+    write_to_file(msg, "data-one.pbf");
+
+    msg.set_s("foobar");
+    write_to_file(msg, "data-string.pbf");
+}
+
diff --git a/test/t/string/testcase.proto b/test/t/string/testcase.proto
new file mode 100644
index 0000000..29601dd
--- /dev/null
+++ b/test/t/string/testcase.proto
@@ -0,0 +1,11 @@
+
+option optimize_for = LITE_RUNTIME;
+
+package TestString;
+
+message Test {
+
+    required string s = 1;
+
+}
+
diff --git a/test/t/string/writer_test_cases.cpp b/test/t/string/writer_test_cases.cpp
new file mode 100644
index 0000000..145fd78
--- /dev/null
+++ b/test/t/string/writer_test_cases.cpp
@@ -0,0 +1,38 @@
+
+#include <test.hpp>
+
+#include "test/t/string/testcase.pb.h"
+
+TEST_CASE("write string field and check with libprotobuf") {
+
+    std::string buffer;
+    protozero::pbf_writer pw(buffer);
+
+    TestString::Test msg;
+
+    SECTION("empty") {
+        pw.add_string(1, "");
+
+        msg.ParseFromString(buffer);
+
+        REQUIRE(msg.s() == "");
+    }
+
+    SECTION("one") {
+        pw.add_string(1, "x");
+
+        msg.ParseFromString(buffer);
+
+        REQUIRE(msg.s() == "x");
+    }
+
+    SECTION("string") {
+        pw.add_string(1, "foobar");
+
+        msg.ParseFromString(buffer);
+
+        REQUIRE(msg.s() == "foobar");
+    }
+
+}
+
diff --git a/test/t/tags/data-tag-1.pbf b/test/t/tags/data-tag-1.pbf
new file mode 100644
index 0000000..83e27c1
--- /dev/null
+++ b/test/t/tags/data-tag-1.pbf
@@ -0,0 +1 @@
+�
\ No newline at end of file
diff --git a/test/t/tags/data-tag-200.pbf b/test/t/tags/data-tag-200.pbf
new file mode 100644
index 0000000..865ec59
--- /dev/null
+++ b/test/t/tags/data-tag-200.pbf
@@ -0,0 +1 @@
+�
�
\ No newline at end of file
diff --git a/test/t/tags/data-tag-200000.pbf b/test/t/tags/data-tag-200000.pbf
new file mode 100644
index 0000000..cd27714
--- /dev/null
+++ b/test/t/tags/data-tag-200000.pbf
@@ -0,0 +1 @@
+��a�
\ No newline at end of file
diff --git a/test/t/tags/data-tag-max.pbf b/test/t/tags/data-tag-max.pbf
new file mode 100644
index 0000000..6b03898
--- /dev/null
+++ b/test/t/tags/data-tag-max.pbf
@@ -0,0 +1 @@
+�����
\ No newline at end of file
diff --git a/test/t/tags/test_cases.cpp b/test/t/tags/test_cases.cpp
new file mode 100644
index 0000000..f330dfd
--- /dev/null
+++ b/test/t/tags/test_cases.cpp
@@ -0,0 +1,59 @@
+
+#include <test.hpp>
+
+inline void check_tag(const std::string& buffer, protozero::pbf_tag_type tag) {
+    protozero::pbf_reader item(buffer);
+
+    REQUIRE(item.next());
+    REQUIRE(item.tag() == tag);
+    REQUIRE(item.get_int32() == 333L);
+    REQUIRE(!item.next());
+}
+
+TEST_CASE("read tags") {
+
+    SECTION("tag 1") {
+        check_tag(load_data("tags/data-tag-1"), 1L);
+    }
+
+    SECTION("tag 200") {
+        check_tag(load_data("tags/data-tag-200"), 200L);
+    }
+
+    SECTION("tag 200000") {
+        check_tag(load_data("tags/data-tag-200000"), 200000L);
+    }
+
+    SECTION("tag max") {
+        check_tag(load_data("tags/data-tag-max"), (1L << 29) - 1);
+    }
+
+}
+
+TEST_CASE("write tags") {
+
+    std::string buffer;
+    protozero::pbf_writer pw(buffer);
+
+    SECTION("tag 1") {
+        pw.add_int32(1L, 333L);
+        REQUIRE(buffer == load_data("tags/data-tag-1"));
+    }
+
+    SECTION("tag 200") {
+        pw.add_int32(200L, 333L);
+        REQUIRE(buffer == load_data("tags/data-tag-200"));
+    }
+
+    SECTION("tag 200000") {
+        pw.add_int32(200000L, 333L);
+        REQUIRE(buffer == load_data("tags/data-tag-200000"));
+    }
+
+    SECTION("tag max") {
+        pw.add_int32((1L << 29) - 1, 333L);
+        REQUIRE(buffer == load_data("tags/data-tag-max"));
+    }
+
+}
+
diff --git a/test/t/tags/testcase.cpp b/test/t/tags/testcase.cpp
new file mode 100644
index 0000000..d5724e9
--- /dev/null
+++ b/test/t/tags/testcase.cpp
@@ -0,0 +1,32 @@
+
+#include <testcase.hpp>
+#include "testcase.pb.h"
+
+int main(int c, char *argv[]) {
+
+    {
+        TestTags::Test1 msg;
+        msg.set_i(333);
+        write_to_file(msg, "data-tag-1.pbf");
+    }
+
+    {
+        TestTags::Test200 msg;
+        msg.set_i(333);
+        write_to_file(msg, "data-tag-200.pbf");
+    }
+
+    {
+        TestTags::Test200000 msg;
+        msg.set_i(333);
+        write_to_file(msg, "data-tag-200000.pbf");
+    }
+
+    {
+        TestTags::TestMax msg;
+        msg.set_i(333);
+        write_to_file(msg, "data-tag-max.pbf");
+    }
+
+}
+
diff --git a/test/t/tags/testcase.proto b/test/t/tags/testcase.proto
new file mode 100644
index 0000000..5251af8
--- /dev/null
+++ b/test/t/tags/testcase.proto
@@ -0,0 +1,29 @@
+
+option optimize_for = LITE_RUNTIME;
+
+package TestTags;
+
+message Test1 {
+
+    required int32 i = 1;
+
+}
+
+message Test200 {
+
+    required int32 i = 200;
+
+}
+
+message Test200000 {
+
+    required int32 i = 200000;
+
+}
+
+message TestMax {
+
+    required int32 i = 536870911;
+
+}
+
diff --git a/test/t/uint32/data-max.pbf b/test/t/uint32/data-max.pbf
new file mode 100644
index 0000000..909ea9a
--- /dev/null
+++ b/test/t/uint32/data-max.pbf
@@ -0,0 +1 @@
+����
\ No newline at end of file
diff --git a/test/t/uint32/data-pos.pbf b/test/t/uint32/data-pos.pbf
new file mode 100644
index 0000000..e19a122
--- /dev/null
+++ b/test/t/uint32/data-pos.pbf
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/test/t/uint32/data-zero.pbf b/test/t/uint32/data-zero.pbf
new file mode 100644
index 0000000..268720a
Binary files /dev/null and b/test/t/uint32/data-zero.pbf differ
diff --git a/test/t/uint32/test_cases.cpp b/test/t/uint32/test_cases.cpp
new file mode 100644
index 0000000..deb0a5b
--- /dev/null
+++ b/test/t/uint32/test_cases.cpp
@@ -0,0 +1,69 @@
+
+#include <test.hpp>
+
+TEST_CASE("read uint32 field") {
+
+    SECTION("zero") {
+        std::string buffer = load_data("uint32/data-zero");
+
+        protozero::pbf_reader item(buffer);
+
+        REQUIRE(item.next());
+        REQUIRE(item.get_uint32() == 0ul);
+        REQUIRE(!item.next());
+    }
+
+    SECTION("positive") {
+        std::string buffer = load_data("uint32/data-pos");
+
+        protozero::pbf_reader item(buffer);
+
+        REQUIRE(item.next());
+        REQUIRE(item.get_uint32() == 1ul);
+        REQUIRE(!item.next());
+    }
+
+    SECTION("max") {
+        std::string buffer = load_data("uint32/data-max");
+
+        protozero::pbf_reader item(buffer);
+
+        REQUIRE(item.next());
+        REQUIRE(item.get_uint32() == std::numeric_limits<uint32_t>::max());
+        REQUIRE(!item.next());
+    }
+
+    SECTION("end_of_buffer") {
+        std::string buffer = load_data("uint32/data-max");
+
+        for (size_t i=1; i < buffer.size(); ++i) {
+            protozero::pbf_reader item(buffer.data(), i);
+            REQUIRE(item.next());
+            REQUIRE_THROWS_AS(item.get_uint32(), protozero::end_of_buffer_exception);
+        }
+    }
+
+}
+
+TEST_CASE("write uint32 field") {
+
+    std::string buffer;
+    protozero::pbf_writer pw(buffer);
+
+    SECTION("zero") {
+        pw.add_uint32(1, 0UL);
+        REQUIRE(buffer == load_data("uint32/data-zero"));
+    }
+
+    SECTION("positive") {
+        pw.add_uint32(1, 1UL);
+        REQUIRE(buffer == load_data("uint32/data-pos"));
+    }
+
+    SECTION("max") {
+        pw.add_uint32(1, std::numeric_limits<uint32_t>::max());
+        REQUIRE(buffer == load_data("uint32/data-max"));
+    }
+
+}
+
diff --git a/test/t/uint32/testcase.cpp b/test/t/uint32/testcase.cpp
new file mode 100644
index 0000000..4a6177b
--- /dev/null
+++ b/test/t/uint32/testcase.cpp
@@ -0,0 +1,17 @@
+
+#include <testcase.hpp>
+#include "testcase.pb.h"
+
+int main(int c, char *argv[]) {
+    TestUInt32::Test msg;
+
+    msg.set_i(0ul);
+    write_to_file(msg, "data-zero.pbf");
+
+    msg.set_i(1);
+    write_to_file(msg, "data-pos.pbf");
+
+    msg.set_i(std::numeric_limits<uint32_t>::max());
+    write_to_file(msg, "data-max.pbf");
+}
+
diff --git a/test/t/uint32/testcase.proto b/test/t/uint32/testcase.proto
new file mode 100644
index 0000000..dcca91b
--- /dev/null
+++ b/test/t/uint32/testcase.proto
@@ -0,0 +1,11 @@
+
+option optimize_for = LITE_RUNTIME;
+
+package TestUInt32;
+
+message Test {
+
+    required uint32 i = 1;
+
+}
+
diff --git a/test/t/uint64/data-max.pbf b/test/t/uint64/data-max.pbf
new file mode 100644
index 0000000..1c2f041
--- /dev/null
+++ b/test/t/uint64/data-max.pbf
@@ -0,0 +1 @@
+���������
\ No newline at end of file
diff --git a/test/t/uint64/data-pos.pbf b/test/t/uint64/data-pos.pbf
new file mode 100644
index 0000000..e19a122
--- /dev/null
+++ b/test/t/uint64/data-pos.pbf
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/test/t/uint64/data-zero.pbf b/test/t/uint64/data-zero.pbf
new file mode 100644
index 0000000..268720a
Binary files /dev/null and b/test/t/uint64/data-zero.pbf differ
diff --git a/test/t/uint64/test_cases.cpp b/test/t/uint64/test_cases.cpp
new file mode 100644
index 0000000..4d7c6f2
--- /dev/null
+++ b/test/t/uint64/test_cases.cpp
@@ -0,0 +1,69 @@
+
+#include <test.hpp>
+
+TEST_CASE("read uint64 field") {
+
+    SECTION("zero") {
+        std::string buffer = load_data("uint64/data-zero");
+
+        protozero::pbf_reader item(buffer);
+
+        REQUIRE(item.next());
+        REQUIRE(item.get_uint64() == 0ul);
+        REQUIRE(!item.next());
+    }
+
+    SECTION("positive") {
+        std::string buffer = load_data("uint64/data-pos");
+
+        protozero::pbf_reader item(buffer);
+
+        REQUIRE(item.next());
+        REQUIRE(item.get_uint64() == 1ul);
+        REQUIRE(!item.next());
+    }
+
+    SECTION("max") {
+        std::string buffer = load_data("uint64/data-max");
+
+        protozero::pbf_reader item(buffer);
+
+        REQUIRE(item.next());
+        REQUIRE(item.get_uint64() == std::numeric_limits<uint64_t>::max());
+        REQUIRE(!item.next());
+    }
+
+    SECTION("end_of_buffer") {
+        std::string buffer = load_data("uint64/data-max");
+
+        for (size_t i=1; i < buffer.size(); ++i) {
+            protozero::pbf_reader item(buffer.data(), i);
+            REQUIRE(item.next());
+            REQUIRE_THROWS_AS(item.get_uint64(), protozero::end_of_buffer_exception);
+        }
+    }
+
+}
+
+TEST_CASE("write uint64 field") {
+
+    std::string buffer;
+    protozero::pbf_writer pw(buffer);
+
+    SECTION("zero") {
+        pw.add_uint64(1, 0UL);
+        REQUIRE(buffer == load_data("uint64/data-zero"));
+    }
+
+    SECTION("positive") {
+        pw.add_uint64(1, 1UL);
+        REQUIRE(buffer == load_data("uint64/data-pos"));
+    }
+
+    SECTION("max") {
+        pw.add_uint64(1, std::numeric_limits<uint64_t>::max());
+        REQUIRE(buffer == load_data("uint64/data-max"));
+    }
+
+}
+
diff --git a/test/t/uint64/testcase.cpp b/test/t/uint64/testcase.cpp
new file mode 100644
index 0000000..22ee8a8
--- /dev/null
+++ b/test/t/uint64/testcase.cpp
@@ -0,0 +1,17 @@
+
+#include <testcase.hpp>
+#include "testcase.pb.h"
+
+int main(int c, char *argv[]) {
+    TestUInt64::Test msg;
+
+    msg.set_i(0ul);
+    write_to_file(msg, "data-zero.pbf");
+
+    msg.set_i(1);
+    write_to_file(msg, "data-pos.pbf");
+
+    msg.set_i(std::numeric_limits<uint64_t>::max());
+    write_to_file(msg, "data-max.pbf");
+}
+
diff --git a/test/t/uint64/testcase.proto b/test/t/uint64/testcase.proto
new file mode 100644
index 0000000..e0524c0
--- /dev/null
+++ b/test/t/uint64/testcase.proto
@@ -0,0 +1,11 @@
+
+option optimize_for = LITE_RUNTIME;
+
+package TestUInt64;
+
+message Test {
+
+    required uint64 i = 1;
+
+}
+
diff --git a/test/t/varint/test_cases.cpp b/test/t/varint/test_cases.cpp
new file mode 100644
index 0000000..3ada39a
--- /dev/null
+++ b/test/t/varint/test_cases.cpp
@@ -0,0 +1,55 @@
+
+#include <test.hpp>
+
+TEST_CASE("varint") {
+
+    std::string buffer;
+    protozero::pbf_writer pw(buffer);
+
+    SECTION("encode/decode int32") {
+        pw.add_int32(1, 17);
+        protozero::pbf_reader item(buffer);
+        REQUIRE(item.next());
+        REQUIRE(17 == item.get_int32());
+    }
+
+    SECTION("encode/decode uint32") {
+        pw.add_uint32(1, 17U);
+        protozero::pbf_reader item(buffer);
+        REQUIRE(item.next());
+        REQUIRE(17U == item.get_uint32());
+    }
+
+    SECTION("encode/decode uint64") {
+        pw.add_uint64(1, (1ULL << 40));
+        protozero::pbf_reader item(buffer);
+        REQUIRE(item.next());
+        REQUIRE((1ULL << 40) == item.get_uint64());
+    }
+
+    SECTION("short buffer while parsing varint") {
+        pw.add_uint64(1, (1ULL << 40));
+        buffer.resize(buffer.size() - 1); // "remove" last byte from buffer
+        protozero::pbf_reader item(buffer);
+        REQUIRE(item.next());
+        REQUIRE_THROWS_AS(item.get_uint64(), protozero::end_of_buffer_exception);
+    }
+
+    SECTION("data corruption in buffer while parsing varint)") {
+        pw.add_uint64(1, (1ULL << 20));
+        buffer[buffer.size() - 1] += 0x80; // pretend the varint goes on
+        protozero::pbf_reader item(buffer);
+        REQUIRE(item.next());
+        REQUIRE_THROWS_AS(item.get_uint64(), protozero::end_of_buffer_exception);
+    }
+
+    SECTION("data corruption in buffer while parsing varint (max length varint)") {
+        pw.add_uint64(1, std::numeric_limits<uint64_t>::max());
+        buffer[buffer.size() - 1] += 0x80; // pretend the varint goes on
+        protozero::pbf_reader item(buffer);
+        REQUIRE(item.next());
+        REQUIRE_THROWS_AS(item.get_uint64(), protozero::varint_too_long_exception);
+    }
+
+}
+
diff --git a/test/t/vector_tile/data.vector.pbf b/test/t/vector_tile/data.vector.pbf
new file mode 100644
index 0000000..5c5b19f
Binary files /dev/null and b/test/t/vector_tile/data.vector.pbf differ
diff --git a/test/t/vector_tile/test_cases.cpp b/test/t/vector_tile/test_cases.cpp
new file mode 100644
index 0000000..6c18c2e
--- /dev/null
+++ b/test/t/vector_tile/test_cases.cpp
@@ -0,0 +1,112 @@
+
+#include <test.hpp>
+
+#include <string>
+#include <vector>
+
+// Input data.vector is encoded according to
+// https://github.com/mapbox/mapnik-vector-tile/blob/master/proto/vector_tile.proto
+
+static std::vector<std::string> expected_layer_names = {
+    "landuse", "waterway", "water", "aeroway", "barrier_line", "building",
+    "landuse_overlay", "tunnel", "road", "bridge", "admin",
+    "country_label_line", "country_label", "marine_label", "state_label",
+    "place_label", "water_label", "area_label", "rail_station_label",
+    "airport_label", "road_label", "waterway_label", "building_label"
+};
+
+namespace {
+
+std::string get_name(protozero::pbf_reader layer) { // copy!
+    while (layer.next(1)) { // required string name
+        return layer.get_string();
+    }
+    return "";
+}
+
+} // anon namespace
+
+TEST_CASE("reading vector tiles") {
+
+    SECTION("iterate over message using next()") {
+        std::string buffer = load_data("vector_tile/data.vector");
+
+        protozero::pbf_reader item(buffer);
+
+        std::vector<std::string> layer_names;
+        while (item.next()) {
+            if (item.tag() == 3) { // repeated message Layer
+                protozero::pbf_reader layer { item.get_message() };
+                while (layer.next()) {
+                    switch (layer.tag()) {
+                        case 1: // required string name
+                            layer_names.push_back(layer.get_string());
+                            break;
+                        default:
+                            layer.skip();
+                    }
+                }
+            } else {
+                item.skip();
+            }
+        }
+
+        REQUIRE(layer_names == expected_layer_names);
+    }
+
+    SECTION("iterate over message using next(type)") {
+        std::string buffer = load_data("vector_tile/data.vector");
+
+        protozero::pbf_reader item(buffer);
+
+        std::vector<std::string> layer_names;
+        while (item.next(3)) { // repeated message Layer
+            protozero::pbf_reader layermsg { item.get_message() };
+            while (layermsg.next(1)) { // required string name
+                layer_names.push_back(layermsg.get_string());
+            }
+        }
+
+        REQUIRE(layer_names == expected_layer_names);
+    }
+
+    SECTION("iterate over features in road layer") {
+        std::string buffer = load_data("vector_tile/data.vector");
+
+        protozero::pbf_reader item(buffer);
+
+        int n=0;
+        while (item.next(3)) { // repeated message Layer
+            protozero::pbf_reader layer { item.get_message() };
+            std::string name = get_name(layer);
+            if (name == "road") {
+                while (layer.next(2)) { // repeated Feature
+                    ++n;
+                    protozero::pbf_reader feature { layer.get_message() };
+                    while (feature.next()) {
+                        switch (feature.tag()) {
+                            case 1: { // optional uint64 id
+                                auto id = feature.get_uint64();
+                                REQUIRE(id >=   1ULL);
+                                REQUIRE(id <= 504ULL);
+                                break;
+                            }
+                            case 3: { // optional GeomType
+                                auto geom_type = feature.get_uint32();
+                                REQUIRE(geom_type >= 1UL);
+                                REQUIRE(geom_type <= 3UL);
+                                break;
+                            }
+                            default:
+                                feature.skip();
+                        }
+                    }
+                }
+            }
+        }
+
+        REQUIRE(n == 502);
+    }
+
+}
+
diff --git a/test/t/zigzag/test_cases.cpp b/test/t/zigzag/test_cases.cpp
new file mode 100644
index 0000000..1773100
--- /dev/null
+++ b/test/t/zigzag/test_cases.cpp
@@ -0,0 +1,51 @@
+
+#include <test.hpp>
+
+inline int32_t zz32(int32_t val) {
+    return protozero::decode_zigzag32(protozero::encode_zigzag32(val));
+}
+
+inline int64_t zz64(int64_t val) {
+    return protozero::decode_zigzag64(protozero::encode_zigzag64(val));
+}
+
+TEST_CASE("zigzag") {
+
+    SECTION("some values - 32bit") {
+
+        REQUIRE(protozero::encode_zigzag32( 0L) == 0UL);
+        REQUIRE(protozero::encode_zigzag32(-1L) == 1UL);
+        REQUIRE(protozero::encode_zigzag32( 1L) == 2UL);
+        REQUIRE(protozero::encode_zigzag32(-2L) == 3UL);
+        REQUIRE(protozero::encode_zigzag32( 2L) == 4UL);
+
+    }
+
+    SECTION("some values - 64bit") {
+
+        REQUIRE(protozero::encode_zigzag64( 0LL) == 0ULL);
+        REQUIRE(protozero::encode_zigzag64(-1LL) == 1ULL);
+        REQUIRE(protozero::encode_zigzag64( 1LL) == 2ULL);
+        REQUIRE(protozero::encode_zigzag64(-2LL) == 3ULL);
+        REQUIRE(protozero::encode_zigzag64( 2LL) == 4ULL);
+
+    }
+
+    SECTION("zigzag and back - 32bit") {
+        REQUIRE(zz32( 0L) ==  0L);
+        REQUIRE(zz32( 1L) ==  1L);
+        REQUIRE(zz32(-1L) == -1L);
+        REQUIRE(zz32(std::numeric_limits<int32_t>::max()) == std::numeric_limits<int32_t>::max());
+        REQUIRE(zz32(std::numeric_limits<int32_t>::min()) == std::numeric_limits<int32_t>::min());
+    }
+
+    SECTION("zigzag and back - 64bit") {
+        REQUIRE(zz64( 0LL) ==  0LL);
+        REQUIRE(zz64( 1LL) ==  1LL);
+        REQUIRE(zz64(-1LL) == -1LL);
+        REQUIRE(zz64(std::numeric_limits<int64_t>::max()) == std::numeric_limits<int64_t>::max());
+        REQUIRE(zz64(std::numeric_limits<int64_t>::min()) == std::numeric_limits<int64_t>::min());
+    }
+
+}
+
diff --git a/test/tests.cpp b/test/tests.cpp
new file mode 100644
index 0000000..378e07b
--- /dev/null
+++ b/test/tests.cpp
@@ -0,0 +1,24 @@
+
+#include <fstream>
+#include <stdexcept>
+#include <string>
+
+#define CATCH_CONFIG_MAIN
+#include <test.hpp> // IWYU pragma: keep
+
+std::string load_data(const std::string& filename) {
+    std::string fullname("test/t/");
+    fullname += filename;
+    fullname += ".pbf";
+
+    std::ifstream stream(fullname.c_str(), std::ios_base::in|std::ios_base::binary);
+    if (!stream.is_open())
+    {
+        throw std::runtime_error("could not open: '" + filename + "'");
+    }
+    std::string buffer(std::istreambuf_iterator<char>(stream.rdbuf()), (std::istreambuf_iterator<char>()));
+    stream.close();
+
+    return buffer;
+}
+
diff --git a/test/writer_tests.cpp b/test/writer_tests.cpp
new file mode 100644
index 0000000..fda3581
--- /dev/null
+++ b/test/writer_tests.cpp
@@ -0,0 +1,4 @@
+
+#define CATCH_CONFIG_MAIN
+#include <test.hpp> // IWYU pragma: keep
+
diff --git a/tutorial.md b/tutorial.md
new file mode 100644
index 0000000..7084cac
--- /dev/null
+++ b/tutorial.md
@@ -0,0 +1,400 @@
+
+# Protozero Tutorial
+
+## Getting to Know Protocol Buffers
+
+Protozero is a very low level library. You really have to know some of the
+insides of Protocol Buffers to work with it!
+
+So before reading any further in this document, read the following from the
+Protocol Buffer documentation:
+
+* [Developer Guide - Overview](https://developers.google.com/protocol-buffers/docs/overview)
+* [Language Guide](https://developers.google.com/protocol-buffers/docs/proto)
+* [Encoding](https://developers.google.com/protocol-buffers/docs/encoding)
+
+Make sure you understand the basic types of values supported by Protocol
+Buffers. Refer to this
+[handy table](https://developers.google.com/protocol-buffers/docs/proto#scalar)
+if you are getting lost.
+
+
+## Prerequisites
+
+You need a C++11-capable compiler for protozero to work. Copy the files in the
+`include` directory somewhere where your build system can find them.
+
+You always need `protozero/pbf_types.hpp`, `protozero/varint.hpp`, and
+`protozero/exception.hpp`. For reading and writing support you need
+`protozero/pbf_reader.hpp` and `protozero/pbf_writer.hpp`, respectively.
+
+
+## Parsing protobuf-encoded messages
+
+### Using `pbf_reader`
+
+To use the `pbf_reader` class, add this include to your C++ program:
+
+    #include <protozero/pbf_reader.hpp>
+
+The `pbf_reader` class contains asserts that will detect some programming
+errors. We encourage you to compile with asserts enabled in your debug builds.
+
+
+### An Introductory Example
+
+Lets say you have a protocol description in a `.proto` file like this:
+
+    message Example1 {
+        required uint32 x = 1;
+        optional string s = 2;
+        repeated fixed64 r = 17;
+    }
+
+To read messages created according to that description, you will have code that
+looks somewhat like this:
+
+    #include <protozero/pbf_reader.hpp>
+
+    // get data from somewhere into the input string
+    std::string input = get_input_data();
+
+    // initialize pbf message with this data
+    protozero::pbf_reader message(input);
+
+    // iterate over fields in the message
+    while (message.next()) {
+
+        // switch depending on the field tag (the field name is not available)
+        switch (message.tag()) {
+            case 1:
+                // get data for tag 1 (in this case an uint32)
+                auto x = message.get_uint32();
+                break;
+            case 2:
+                // get data for tag 2 (in this case a string)
+                std::string s = message.get_string();
+                break;
+            case 17:
+                // ignore data for tag 17
+                message.skip();
+                break;
+            default:
+                // ignore data for unknown tags to allow for future extensions
+                message.skip();
+        }
+
+    }
+
+You always have to call `next()` and then either one of the accessor functions
+(like `get_uint32()` or `get_string()`) to get the field value or `skip()` to
+ignore this field. Then call `next()` again, and so forth. Never call `next()`
+twice in a row or any if the accessor or skip functions twice in a row.
+
+Because the `pbf_reader` class doesn't know the `.proto` file it doesn't know
+which field names or tags there are and it doesn't known the types of the
+fields. You have to make sure to call the right `get_...()` function for each
+tag. Some `assert()s` are done to check you are calling the right functions,
+but not all errors can be detected.
+
+Note that it doesn't matter whether a field is defined as `required`,
+`optional`, or `repeated`. You always have to be prepared to get zero, one, or
+more instances of a field and you always have to be prepared to get other
+fields, too, unless you want your program to break if somebody adds a new
+field.
+
+
+### If You Only Need a Single Field
+
+If, out of a protocol buffer message, you only need the value of a single
+field, you can use the version of the `next()` function with a parameter:
+
+    // same .proto file and initialization as above
+
+    // get all fields with tag 17, skip all others
+    while (message.next(17)) {
+        auto r = message.get_fixed64();
+        std::cout << r << "\n";
+    }
+
+
+### Handling Scalar Fields
+
+As you saw in the example, handling scalar field types is reasonably easy. You
+just check the `.proto` file for the type of a field and call the corresponding
+function called `get_` + _field type_.
+
+For `string` and `bytes` types the internal handling is exactly the same, but
+both `get_string()` and `get_bytes()` are provided to make the code
+self-documenting. Both theses calls allocate and return a `std::string` which
+can add some overhead. You can call the `get_data()` function instead which
+returns a `std::pair<const char*, uint32_t>`, ie a pointer into the data and
+the length of the data.
+
+
+### Handling Repeated Packed Fields
+
+Fields that are marked as `[packed=true]` in the `.proto` file are handled
+somewhat differently. `get_packed_...()` functions returning an iterator pair
+are used to access the data.
+
+So, for example, if you have a protocol description in a `.proto` file like
+this:
+
+    message Example2 {
+        repeated sint32 i = 1 [packed=true];
+    }
+
+You can get to the data like this:
+
+    protozero::pbf_reader message(input.data(), input.size());
+
+    // set current field
+    item.next(1);
+
+    // get an iterator pair
+    auto pi = item.get_packed_sint32();
+
+    // iterate to get to all values
+    for (auto it = pi.first; it != pi.second; ++it) {
+        std::cout << *it << "\n";
+    }
+
+So you are getting a pair of normal forward iterators that can be used with any
+STL algorithms etc.
+
+Note that the previous only applies to repeated **packed** fields, normal
+repeated fields are handled in the usual way for scalar fields.
+
+
+### Handling Embedded Messages
+
+Protocol Buffers can embed any message inside another message. To access an
+embedded message use the `get_message()` function. So for this description:
+
+    message Point {
+        required double x = 1;
+        required double y = 2;
+    }
+
+    message Example3 {
+        repeated Point point = 10;
+    }
+
+you can parse with this code:
+
+    protozero::pbf_reader message(input);
+
+    while (message.next(10)) {
+        protozero::pbf_reader point = message.get_message();
+        double x, y;
+        while (point.next()) {
+            switch (point.tag()) {
+                case 1:
+                    x = point.get_double();
+                    break;
+                case 2:
+                    y = point.get_double();
+                    break;
+                default:
+                    point.skip();
+            }
+        }
+        std::cout << "x=" << x << " y=" << y << "\n";
+    }
+
+
+### Handling Enums
+
+Enums are stored as varints and they can't be differentiated from them. Use
+the `get_enum()` function to get the value of the enum, you have to translate
+this into the symbolic name yourself. See the `enum` test case for an example.
+
+
+### Exceptions
+
+All exceptions thrown by `pbf_reader.hpp` functions derive from
+`protozero::exception`.
+
+Note that all exceptions can also happen if you are expecting a data field of
+a certain type in your code but the field actually has a different type. In
+that case the `pbf_reader` class might interpret the bytes in the buffer in
+the wrong way and anything can happen.
+
+#### `end_of_buffer_exception`
+
+This will be thrown whenever any of the functions "runs out of input data".
+It means you either have an incomplete message in your input or some other
+data corruption has taken place.
+
+#### `unknown_pbf_wire_type_exception`
+
+This will be thrown if an unsupported wire type is encountered. Either your
+input data is corrupted or it was written with an unsupported version of a
+Protocol Buffers implementation.
+
+#### `varint_too_long_exception`
+
+This exception indicates an illegal encoding of a varint. It means your input
+data is corrupted in some way.
+
+
+### The `pbf_reader` Class
+
+The `pbf_reader` class behaves like a value type. Objects are reasonably small
+(two pointers and two `uint32_t`, so 24 bytes on a 64bit system) and they can
+be copied and moved around trivially.
+
+`pbf_reader` objects can be constructed from a `std::string` or a `const char*`
+and a length field (either supplied as separate arguments or as a `std::pair`).
+In all cases objects of the `pbf_reader` class store a pointer into the input
+data that was given to the constructor. You have to make sure this pointer
+stays valid for the duration of the objects lifetime.
+
+
+## Writing Protobuf-Encoded Messages
+
+### Using `pbf_writer`
+
+To use the `pbf_writer` class, add this include to your C++ program:
+
+    #include <protozero/pbf_writer.hpp>
+
+The `pbf_writer` class contains asserts that will detect some programming
+errors. We encourage you to compile with asserts enabled in your debug builds.
+
+
+### An Introductory Example
+
+Lets say you have a protocol description in a `.proto` file like this:
+
+    message Example {
+        required uint32 x = 1;
+        optional string s = 2;
+        repeated fixed64 r = 17;
+    }
+
+To write messages created according to that description, you will have code
+that looks somewhat like this:
+
+    #include <protozero/pbf_writer.hpp>
+
+    std::string data;
+    osmium::util::pbf_writer pbf_example(data);
+
+    pbf_example.add_uint32(1, 27);       // uint32_t x
+    pbf_example.add_fixed64(17, 1);      // fixed64 r
+    pbf_example.add_fixed64(17, 2);
+    pbf_example.add_fixed64(17, 3);
+    pbf_example.add_string(2, "foobar"); // string s
+
+First you create an empty string which will be used as buffer to assemble the
+protobuf-formatted message. The `pbf_writer` object contains a reference to
+this string buffer and through it you add data to that buffer piece by piece.
+
+
+### Handling Scalar Fields
+
+As you could see in the introductory example handling any kind of scalar field
+is easy. The type of field doesn't matter and it doesn't matter whether it is
+optional, required or repeated. You always call one of the `add_TYPE()` method
+on the pbf writer object.
+
+The first parameter of these methods is always the *tag* of the field (the
+field number) from the `.proto` file. The second parameter is the value you
+want to set. For the `bytes` and `string` types several versions of the add
+method are available taking a `const std::string&` or a `const char*` and a
+length.
+
+For `enum` types you have to use the numeric value as the symbolic names from
+the `.proto` file are not available.
+
+
+### Handling Repeated Packed Fields
+
+Repeated packed fields can easily be set from a pair of iterators:
+
+    std::string data;
+    osmium::util::pbf_writer pw(data);
+
+    std::vector<int> v = { 1, 4, 9, 16, 25, 36 };
+    pw.add_packed_int32(1, std::begin(v), std::end(v));
+
+
+### Handling Sub-Messages
+
+Nested sub-messages can be handled by first creating the submessage and then
+adding to the parent message:
+
+    std::string buffer_sub;
+    osmium::util::pbf_writer pbf_sub(buffer_sub);
+
+    // add fields to sub-message
+    pbf_sub.add_...(...);
+    // ...
+
+    // sub-message is finished here
+
+    std::string buffer_parent;
+    osmium::util::pbf_writer pbf_parent(buffer_parent);
+    pbf_parent.add_message(buffer_sub);
+
+This is easy to do but it has the drawback of needing a separate `std::string`
+buffer. If this concerns you (and why would use use protozero and not the
+Google protobuf library if it doesn't) there is another way:
+
+    std::string buffer;
+    osmium::util::pbf_writer pbf_parent(buffer);
+
+    // optionally add fields to parent here
+    pbf_parent.add_...(...);
+
+    // open a new scope
+    {
+        // create new pbf_writer with parent and the tag (field number)
+        // as parameters
+        osmium::util::pbf_writer pbf_sub(pbf_parent, 1);
+
+        // add fields to sub here...
+        pbf_sub.add_...(...);
+
+    } // closing the scope will close the sub-message
+
+    // optionally add more fields to parent here
+    pbf_parent.add_...(...);
+
+This can be nested arbitrarily deep.
+
+Internally the sub-message writer re-uses the buffer from the parent. It
+reserves enough space in the buffer to later write the length of the submessage
+into it. It then adds the contents of the submessage to the buffer. When the
+`pbf_sub` writer is destructed the length of the submessage is calculated and
+written in the reserved space. If less space was needed for the length field
+than was available, the rest of the buffer is moved over a few bytes.
+
+
+## Using the low-level varint and zigzag encoding and decoding functions
+
+Protozero gives you access to the low-level functions for encoding and
+decoding varint and zigzag integer encodings, because these functions can
+sometimes be useful outside the Protocol Buffer context.
+
+### Using low-level functions
+
+To use the low-level, add this include to your C++ program:
+
+    #include <protozero/varint.hpp>
+
+### Functions
+
+The following functions are then available:
+
+    decode_varint()
+    write_varint()
+    encode_zigzag32()
+    encode_zigzag64()
+    decode_zigzag32()
+    decode_zigzag64()
+
+See the reference documentation created by `make doc` for details.
+

-- 
Alioth's /usr/local/bin/git-commit-notice on /srv/git.debian.org/git/pkg-grass/protozero.git



More information about the Pkg-grass-devel mailing list