From a8f711a2f18e8fae0efcd05aff5b673dca5fa0b4 Mon Sep 17 00:00:00 2001
From: Niels Lohmann <mail@nlohmann.me>
Date: Thu, 18 Jan 2018 21:57:21 +0100
Subject: [PATCH] :heavy_plus_sign: using Google Benchmark #921

---
 README.md                                     |    3 +-
 benchmarks/CMakeLists.txt                     |   26 +
 benchmarks/Makefile                           |   21 -
 benchmarks/README.md                          |    3 -
 .../{files => data}/jeopardy/jeopardy.json    |    0
 .../nativejson-benchmark/canada.json          |    0
 .../nativejson-benchmark/citm_catalog.json    |    0
 .../nativejson-benchmark/twitter.json         |    0
 benchmarks/files/numbers/generate.py          |   25 -
 benchmarks/src/benchmarks.cpp                 |  214 ++-
 benchmarks/src/benchmarks_simple.cpp          |  158 --
 benchmarks/thirdparty/benchpress/LICENSE      |   21 -
 .../thirdparty/benchpress/benchpress.hpp      |  401 -----
 benchmarks/thirdparty/cxxopts/LICENSE         |   19 -
 benchmarks/thirdparty/cxxopts/cxxopts.hpp     | 1312 -----------------
 develop/json.hpp                              |    2 +-
 src/json.hpp                                  |    2 +-
 test/src/unit-inspection.cpp                  |    4 +-
 18 files changed, 125 insertions(+), 2086 deletions(-)
 create mode 100644 benchmarks/CMakeLists.txt
 delete mode 100644 benchmarks/Makefile
 delete mode 100644 benchmarks/README.md
 rename benchmarks/{files => data}/jeopardy/jeopardy.json (100%)
 rename benchmarks/{files => data}/nativejson-benchmark/canada.json (100%)
 rename benchmarks/{files => data}/nativejson-benchmark/citm_catalog.json (100%)
 rename benchmarks/{files => data}/nativejson-benchmark/twitter.json (100%)
 delete mode 100755 benchmarks/files/numbers/generate.py
 delete mode 100644 benchmarks/src/benchmarks_simple.cpp
 delete mode 100644 benchmarks/thirdparty/benchpress/LICENSE
 delete mode 100644 benchmarks/thirdparty/benchpress/benchpress.hpp
 delete mode 100644 benchmarks/thirdparty/cxxopts/LICENSE
 delete mode 100644 benchmarks/thirdparty/cxxopts/cxxopts.hpp

diff --git a/README.md b/README.md
index d5935755..e4c71481 100644
--- a/README.md
+++ b/README.md
@@ -935,7 +935,6 @@ The library itself contains of a single header file licensed under the MIT licen
 - [**American fuzzy lop**](http://lcamtuf.coredump.cx/afl/) for fuzz testing
 - [**AppVeyor**](https://www.appveyor.com) for [continuous integration](https://ci.appveyor.com/project/nlohmann/json) on Windows
 - [**Artistic Style**](http://astyle.sourceforge.net) for automatic source code identation
-- [**benchpress**](https://github.com/sbs-ableton/benchpress) to benchmark the code
 - [**Catch**](https://github.com/philsquared/Catch) for the unit tests
 - [**Clang**](http://clang.llvm.org) for compilation with code sanitizers
 - [**Cmake**](https://cmake.org) for build automation
@@ -943,10 +942,10 @@ The library itself contains of a single header file licensed under the MIT licen
 - [**Coveralls**](https://coveralls.io) to measure [code coverage](https://coveralls.io/github/nlohmann/json)
 - [**Coverity Scan**](https://scan.coverity.com) for [static analysis](https://scan.coverity.com/projects/nlohmann-json)
 - [**cppcheck**](http://cppcheck.sourceforge.net) for static analysis
-- [**cxxopts**](https://github.com/jarro2783/cxxopts) to let benchpress parse command-line parameters
 - [**Doxygen**](http://www.stack.nl/~dimitri/doxygen/) to generate [documentation](https://nlohmann.github.io/json/)
 - [**git-update-ghpages**](https://github.com/rstacruz/git-update-ghpages) to upload the documentation to gh-pages
 - [**GitHub Changelog Generator**](https://github.com/skywinder/github-changelog-generator) to generate the [ChangeLog](https://github.com/nlohmann/json/blob/develop/ChangeLog.md)
+- [**Google Benchmark**]https://github.com/google/benchmark) to implement the benchmarks
 - [**libFuzzer**](http://llvm.org/docs/LibFuzzer.html) to implement fuzz testing for OSS-Fuzz
 - [**OSS-Fuzz**](https://github.com/google/oss-fuzz) for continuous fuzz testing of the library
 - [**Probot**](https://probot.github.io) for automating maintainer tasks such as closing stale issues, requesting missing information, or detecting toxic comments.
diff --git a/benchmarks/CMakeLists.txt b/benchmarks/CMakeLists.txt
new file mode 100644
index 00000000..f614978e
--- /dev/null
+++ b/benchmarks/CMakeLists.txt
@@ -0,0 +1,26 @@
+cmake_minimum_required(VERSION 3.0)
+project(JSON_Benchmarks LANGUAGES CXX)
+
+# set compiler flags
+if((CMAKE_CXX_COMPILER_ID MATCHES GNU) OR (CMAKE_CXX_COMPILER_ID MATCHES Clang))
+   set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11 -flto -DNDEBUG -O3")
+endif()
+
+# configure Google Benchmarks
+set(BENCHMARK_ENABLE_TESTING OFF CACHE INTERNAL "" FORCE)
+add_subdirectory(thirdparty/benchmark)
+
+# header directories
+include_directories(thirdparty)
+include_directories(${CMAKE_SOURCE_DIR}/src)
+
+# copy test files to build folder
+file(COPY ${CMAKE_SOURCE_DIR}/data DESTINATION .)
+file(COPY ${CMAKE_SOURCE_DIR}/../test/data/regression/floats.json
+          ${CMAKE_SOURCE_DIR}/../test/data/regression/unsigned_ints.json
+          ${CMAKE_SOURCE_DIR}/../test/data/regression/signed_ints.json
+    DESTINATION data/numbers)
+
+# benchmark binary
+add_executable(json_benchmarks src/benchmarks.cpp)
+target_link_libraries(json_benchmarks benchmark ${CMAKE_THREAD_LIBS_INIT})
diff --git a/benchmarks/Makefile b/benchmarks/Makefile
deleted file mode 100644
index ef2de8a3..00000000
--- a/benchmarks/Makefile
+++ /dev/null
@@ -1,21 +0,0 @@
-
-#
-# Build/run json.hpp benchmarks, eg. CXX=g++-7 make
-# 
-# The existing json_benchmarks did not allow optimization under some compilers
-# 
-all: json_benchmarks json_benchmarks_simple number_jsons
-	bash -c 'time ./json_benchmarks'
-	bash -c 'time ./json_benchmarks_simple'
-
-json_benchmarks: src/benchmarks.cpp ../src/json.hpp
-	$(CXX) -std=c++11 -pthread $(CXXFLAGS) -DNDEBUG -O3 -flto -I thirdparty/benchpress -I thirdparty/cxxopts -I../src src/benchmarks.cpp $(LDFLAGS) -o $@
-
-json_benchmarks_simple: src/benchmarks_simple.cpp ../src/json.hpp
-	$(CXX) -std=c++11 $(CXXFLAGS) -DNDEBUG -O3 -flto -I../src $(<) $(LDFLAGS) -o $@
-
-number_jsons:
-	(test -e files/numbers/floats.json -a -e files/numbers/signed_ints.json -a -e files/numbers/unsigned_ints.json) || (cd files/numbers ; python generate.py)
-
-clean:
-	rm -f json_benchmarks json_benchmarks_simple files/numbers/*.json
diff --git a/benchmarks/README.md b/benchmarks/README.md
deleted file mode 100644
index 1d3582b4..00000000
--- a/benchmarks/README.md
+++ /dev/null
@@ -1,3 +0,0 @@
-# Bechmarks
-
-Run `make` to compile and run a small set of benchmarks.
diff --git a/benchmarks/files/jeopardy/jeopardy.json b/benchmarks/data/jeopardy/jeopardy.json
similarity index 100%
rename from benchmarks/files/jeopardy/jeopardy.json
rename to benchmarks/data/jeopardy/jeopardy.json
diff --git a/benchmarks/files/nativejson-benchmark/canada.json b/benchmarks/data/nativejson-benchmark/canada.json
similarity index 100%
rename from benchmarks/files/nativejson-benchmark/canada.json
rename to benchmarks/data/nativejson-benchmark/canada.json
diff --git a/benchmarks/files/nativejson-benchmark/citm_catalog.json b/benchmarks/data/nativejson-benchmark/citm_catalog.json
similarity index 100%
rename from benchmarks/files/nativejson-benchmark/citm_catalog.json
rename to benchmarks/data/nativejson-benchmark/citm_catalog.json
diff --git a/benchmarks/files/nativejson-benchmark/twitter.json b/benchmarks/data/nativejson-benchmark/twitter.json
similarity index 100%
rename from benchmarks/files/nativejson-benchmark/twitter.json
rename to benchmarks/data/nativejson-benchmark/twitter.json
diff --git a/benchmarks/files/numbers/generate.py b/benchmarks/files/numbers/generate.py
deleted file mode 100755
index 714ee3a1..00000000
--- a/benchmarks/files/numbers/generate.py
+++ /dev/null
@@ -1,25 +0,0 @@
-#!/usr/bin/env python
-
-import json
-import random
-import sys
-
-random.seed(0)
-
-# floats
-result_floats = []
-for x in range(0, 1000000):
-	result_floats.append(random.uniform(-100000000.0, 100000000.0))
-json.dump(result_floats, open("floats.json", "w"), indent=2)
-
-# unsigned integers
-result_uints = []
-for x in range(0, 1000000):
-	result_uints.append(random.randint(0, 18446744073709551615))
-json.dump(result_uints, open("unsigned_ints.json", "w"), indent=2)
-
-# signed integers
-result_sints = []
-for x in range(0, 1000000):
-	result_sints.append(random.randint(-9223372036854775808, 9223372036854775807))
-json.dump(result_sints, open("signed_ints.json", "w"), indent=2)
diff --git a/benchmarks/src/benchmarks.cpp b/benchmarks/src/benchmarks.cpp
index a76c3783..6dd470d3 100644
--- a/benchmarks/src/benchmarks.cpp
+++ b/benchmarks/src/benchmarks.cpp
@@ -1,132 +1,106 @@
-#define BENCHPRESS_CONFIG_MAIN
-
+#include "benchmark/benchmark.h"
+#include "json.hpp"
 #include <fstream>
-#include <sstream>
-#include <benchpress.hpp>
-#include <json.hpp>
-#include <pthread.h>
-#include <thread>
 
 using json = nlohmann::json;
 
-struct StartUp
+//////////////////////////////////////////////////////////////////////////////
+// parse JSON from file
+//////////////////////////////////////////////////////////////////////////////
+
+static void ParseFile(benchmark::State& state, const char* filename)
 {
-    StartUp()
+    while (state.KeepRunning())
     {
-#ifndef __llvm__
-        // pin thread to a single CPU
-        cpu_set_t cpuset;
-        pthread_t thread;
-        thread = pthread_self();
-        CPU_ZERO(&cpuset);
-        CPU_SET(std::thread::hardware_concurrency() - 1, &cpuset);
-        pthread_setaffinity_np(thread, sizeof(cpu_set_t), &cpuset);
-#endif
+        state.PauseTiming();
+        auto* f = new std::ifstream(filename);
+        auto* j = new json();
+        state.ResumeTiming();
+
+        *j = json::parse(*f);
+
+        state.PauseTiming();
+        delete f;
+        delete j;
+        state.ResumeTiming();
     }
-};
-StartUp startup;
 
-enum class EMode { input, output_no_indent, output_with_indent };
-
-static void bench(benchpress::context& ctx,
-                  const std::string& in_path,
-                  const EMode mode)
-{
-    // using string streams for benchmarking to factor-out cold-cache disk
-    // access.
-#if defined( FROMFILE )
-    std::ifstream istr;
-    {
-        istr.open( in_path, std::ifstream::in );
-
-        // read the stream once
-        json j;
-        istr >> j;
-        // clear flags and rewind
-        istr.clear();
-        istr.seekg(0);
-    }
-#else
-    std::stringstream istr;
-    {
-        // read file into string stream
-        std::ifstream input_file(in_path);
-        istr << input_file.rdbuf();
-        input_file.close();
-
-        // read the stream once
-        json j;
-        istr >> j;
-        // clear flags and rewind
-        istr.clear();
-        istr.seekg(0);
-    }
-#endif
-
-    switch (mode)
-    {
-        // benchmarking input
-        case EMode::input:
-        {
-            ctx.reset_timer();
-
-            for (size_t i = 0; i < ctx.num_iterations(); ++i)
-            {
-                // clear flags and rewind
-                istr.clear();
-                istr.seekg(0);
-                json j;
-                istr >> j;
-            }
-
-            break;
-        }
-
-        // benchmarking output
-        case EMode::output_no_indent:
-        case EMode::output_with_indent:
-        {
-            // create JSON value from input
-            json j;
-            istr >> j;
-            std::stringstream ostr;
-
-            ctx.reset_timer();
-            for (size_t i = 0; i < ctx.num_iterations(); ++i)
-            {
-                if (mode == EMode::output_no_indent)
-                {
-                    ostr << j;
-                }
-                else
-                {
-                    ostr << std::setw(4) << j;
-                }
-
-                // reset data
-                ostr.str(std::string());
-            }
-
-            break;
-        }
-    }
+    std::ifstream file(filename, std::ios::binary | std::ios::ate);
+    state.SetBytesProcessed(state.iterations() * file.tellg());
 }
+BENCHMARK_CAPTURE(ParseFile, jeopardy,      "data/jeopardy/jeopardy.json");
+BENCHMARK_CAPTURE(ParseFile, canada,        "data/nativejson-benchmark/canada.json");
+BENCHMARK_CAPTURE(ParseFile, citm_catalog,  "data/nativejson-benchmark/citm_catalog.json");
+BENCHMARK_CAPTURE(ParseFile, twitter,       "data/nativejson-benchmark/twitter.json");
+BENCHMARK_CAPTURE(ParseFile, floats,        "data/numbers/floats.json");
+BENCHMARK_CAPTURE(ParseFile, signed_ints,   "data/numbers/signed_ints.json");
+BENCHMARK_CAPTURE(ParseFile, unsigned_ints, "data/numbers/unsigned_ints.json");
 
-#define BENCHMARK_I(mode, title, in_path)           \
-    BENCHMARK((title), [](benchpress::context* ctx) \
-    {                                               \
-        bench(*ctx, (in_path), (mode));             \
-    })
 
-BENCHMARK_I(EMode::input, "parse jeopardy.json",              "files/jeopardy/jeopardy.json");
-BENCHMARK_I(EMode::input, "parse canada.json",                "files/nativejson-benchmark/canada.json");
-BENCHMARK_I(EMode::input, "parse citm_catalog.json",          "files/nativejson-benchmark/citm_catalog.json");
-BENCHMARK_I(EMode::input, "parse twitter.json",               "files/nativejson-benchmark/twitter.json");
-BENCHMARK_I(EMode::input, "parse numbers/floats.json",        "files/numbers/floats.json");
-BENCHMARK_I(EMode::input, "parse numbers/signed_ints.json",   "files/numbers/signed_ints.json");
-BENCHMARK_I(EMode::input, "parse numbers/unsigned_ints.json", "files/numbers/unsigned_ints.json");
+//////////////////////////////////////////////////////////////////////////////
+// parse JSON from string
+//////////////////////////////////////////////////////////////////////////////
 
-BENCHMARK_I(EMode::output_no_indent,   "dump jeopardy.json",             "files/jeopardy/jeopardy.json");
-BENCHMARK_I(EMode::output_with_indent, "dump jeopardy.json with indent", "files/jeopardy/jeopardy.json");
-BENCHMARK_I(EMode::output_no_indent,   "dump numbers/floats.json",       "files/numbers/floats.json");
-BENCHMARK_I(EMode::output_no_indent,   "dump numbers/signed_ints.json",  "files/numbers/signed_ints.json");
+static void ParseString(benchmark::State& state, const char* filename)
+{
+    std::ifstream f(filename);
+    std::string str((std::istreambuf_iterator<char>(f)), std::istreambuf_iterator<char>());
+
+    while (state.KeepRunning())
+    {
+        state.PauseTiming();
+        auto* j = new json();
+        state.ResumeTiming();
+
+        *j = json::parse(str);
+
+        state.PauseTiming();
+        delete j;
+        state.ResumeTiming();
+    }
+
+    state.SetBytesProcessed(state.iterations() * str.size());
+}
+BENCHMARK_CAPTURE(ParseString, jeopardy,      "data/jeopardy/jeopardy.json");
+BENCHMARK_CAPTURE(ParseString, canada,        "data/nativejson-benchmark/canada.json");
+BENCHMARK_CAPTURE(ParseString, citm_catalog,  "data/nativejson-benchmark/citm_catalog.json");
+BENCHMARK_CAPTURE(ParseString, twitter,       "data/nativejson-benchmark/twitter.json");
+BENCHMARK_CAPTURE(ParseString, floats,        "data/numbers/floats.json");
+BENCHMARK_CAPTURE(ParseString, signed_ints,   "data/numbers/signed_ints.json");
+BENCHMARK_CAPTURE(ParseString, unsigned_ints, "data/numbers/unsigned_ints.json");
+
+
+//////////////////////////////////////////////////////////////////////////////
+// serialize JSON
+//////////////////////////////////////////////////////////////////////////////
+
+static void Dump(benchmark::State& state, const char* filename, int indent)
+{
+    std::ifstream f(filename);
+    std::string str((std::istreambuf_iterator<char>(f)), std::istreambuf_iterator<char>());
+    json j = json::parse(str);
+
+    while (state.KeepRunning())
+    {
+        j.dump(indent);
+    }
+
+    state.SetBytesProcessed(state.iterations() * j.dump(indent).size());
+}
+BENCHMARK_CAPTURE(Dump, jeopardy / -,      "data/jeopardy/jeopardy.json",                 -1);
+BENCHMARK_CAPTURE(Dump, jeopardy / 4,      "data/jeopardy/jeopardy.json",                 4);
+BENCHMARK_CAPTURE(Dump, canada / -,        "data/nativejson-benchmark/canada.json",       -1);
+BENCHMARK_CAPTURE(Dump, canada / 4,        "data/nativejson-benchmark/canada.json",       4);
+BENCHMARK_CAPTURE(Dump, citm_catalog / -,  "data/nativejson-benchmark/citm_catalog.json", -1);
+BENCHMARK_CAPTURE(Dump, citm_catalog / 4,  "data/nativejson-benchmark/citm_catalog.json", 4);
+BENCHMARK_CAPTURE(Dump, twitter / -,       "data/nativejson-benchmark/twitter.json",      -1);
+BENCHMARK_CAPTURE(Dump, twitter / 4,       "data/nativejson-benchmark/twitter.json",      4);
+BENCHMARK_CAPTURE(Dump, floats / -,        "data/numbers/floats.json",                    -1);
+BENCHMARK_CAPTURE(Dump, floats / 4,        "data/numbers/floats.json",                    4);
+BENCHMARK_CAPTURE(Dump, signed_ints / -,   "data/numbers/signed_ints.json",               -1);
+BENCHMARK_CAPTURE(Dump, signed_ints / 4,   "data/numbers/signed_ints.json",               4);
+BENCHMARK_CAPTURE(Dump, unsigned_ints / -, "data/numbers/unsigned_ints.json",             -1);
+BENCHMARK_CAPTURE(Dump, unsigned_ints / 4, "data/numbers/unsigned_ints.json",             4);
+
+
+BENCHMARK_MAIN();
diff --git a/benchmarks/src/benchmarks_simple.cpp b/benchmarks/src/benchmarks_simple.cpp
deleted file mode 100644
index 4fad680a..00000000
--- a/benchmarks/src/benchmarks_simple.cpp
+++ /dev/null
@@ -1,158 +0,0 @@
-// 
-// benchmarks_simple.cpp -- a less complex version of benchmarks.cpp, that better reflects actual performance
-// 
-//     For some reason, the complexity of benchmarks.cpp doesn't allow
-// the compiler to optimize code using json.hpp effectively.  The
-// exact same tests, with the use of benchpress and cxxopts produces
-// much faster code, at least under g++.
-// 
-#include <fstream>
-#include <iostream>
-#include <chrono>
-#include <list>
-#include <tuple>
-
-#include <json.hpp>
-
-using json = nlohmann::json;
-
-enum class EMode { input, output, indent };
-
-static double bench(const EMode mode, size_t iters, const std::string& in_path )
-{
-    // using string streams for benchmarking to factor-out cold-cache disk
-    // access.  Define FROMFILE to use file I/O instead.
-#if defined( FROMFILE )
-    std::ifstream istr;
-    {
-        istr.open( in_path, std::ifstream::in );
-
-        // read the stream once
-        json j;
-        istr >> j;
-        // clear flags and rewind
-        istr.clear();
-        istr.seekg(0);
-    }
-#else
-    std::stringstream istr;
-    {
-        // read file into string stream
-        std::ifstream input_file(in_path);
-        istr << input_file.rdbuf();
-        input_file.close();
-
-        // read the stream once
-        json j;
-        istr >> j;
-        // clear flags and rewind
-        istr.clear();
-        istr.seekg(0);
-    }
-#endif
-    double tps = 0;
-    switch (mode)
-    {
-        // benchmarking input
-        case EMode::input:
-        {
-	    auto start = std::chrono::system_clock::now();
-            for (size_t i = 0; i < iters; ++i)
-            {
-                // clear flags and rewind
-                istr.clear();
-                istr.seekg(0);
-                json j;
-                istr >> j;
-            }
-	    auto ended = std::chrono::system_clock::now();
-	    tps = 1.0 / std::chrono::duration<double>( ended - start ).count();
-            break;
-        }
-
-        // benchmarking output
-        case EMode::output:
-        case EMode::indent:
-        {
-            // create JSON value from input
-            json j;
-            istr >> j;
-            std::stringstream ostr;
-
-	    auto start = std::chrono::system_clock::now();
-            for (size_t i = 0; i < iters; ++i)
-            {
-                if (mode == EMode::indent)
-                {
-                    ostr << j;
-                }
-                else
-                {
-                    ostr << std::setw(4) << j;
-                }
-
-                // reset data
-                ostr.str(std::string());
-            }
-	    auto ended = std::chrono::system_clock::now();
-	    tps = 1.0 / std::chrono::duration<double>( ended - start ).count();
-
-            break;
-        }
-    }
-    return tps;
-}
-
-template <typename T>
-struct average {
-    T _sum { 0 };
-    size_t _count { 0 };
-    T operator+=( const T &val_ ) { _sum += val_; +_count++; return val_; }
-    operator T() { return _sum / _count; }
-};
-
-// Execute each test approximately enough times to get near 1
-// transaction per second, and compute the average; a single aggregate
-// number that gives a performance metric representing both parsing
-// and output.
-
-int main( int, char ** )
-{
-    std::list<std::tuple<std::string, EMode, size_t, std::string>> tests {
-	{ "parse jeopardy.json",	EMode::input,   2, "files/jeopardy/jeopardy.json" },
-	{ "parse canada.json",		EMode::input,  30, "files/nativejson-benchmark/canada.json" },
-	{ "parse citm_catalog.json",	EMode::input, 120, "files/nativejson-benchmark/citm_catalog.json" },
-	{ "parse twitter.json",		EMode::input, 225, "files/nativejson-benchmark/twitter.json" },
-	{ "parse floats.json",		EMode::input,   5, "files/numbers/floats.json" },
-	{ "parse signed_ints.json",	EMode::input,   6, "files/numbers/signed_ints.json" },
-	{ "parse unsigned_ints.json",	EMode::input,   6, "files/numbers/unsigned_ints.json" },
-	{ "dump jeopardy.json",		EMode::output,  5, "files/jeopardy/jeopardy.json" },
-	{ "dump jeopardy.json w/ind.",	EMode::indent,  5, "files/jeopardy/jeopardy.json" },
-	{ "dump floats.json",		EMode::output,  2, "files/numbers/floats.json" },
-	{ "dump signed_ints.json",	EMode::output, 20, "files/numbers/signed_ints.json" },
-    };
-    
-    average<double> avg;
-    for ( auto t : tests ) {
-	std::string name, path;
-	EMode mode;
-	size_t iters;
-	std::tie(name, mode, iters, path) = t;
-	auto tps = bench( mode, iters, path );
-	avg += tps;
-	std::cout
-	    << std::left 
-	    << std::setw( 30 ) << name
-	    << std::right	    
-	    << " x " 	<< std::setw(  3 ) << iters
-	    << std::left
-	    << " == " 	<< std::setw( 10 ) << tps
-	    << std::right
-	    << " TPS, "	<< std::setw(  8 ) << std::round( tps * 1e6 / iters )
-	    << " ms/op"
-	    << std::endl;
-    }
-    std::cout << std::setw( 40 ) << "" << std::string( 10, '-' ) << std::endl;
-    std::cout << std::setw( 40 ) << "" << std::setw( 10 ) << std::left << avg << " TPS Average" << std::endl;
-    return 0;
-}
diff --git a/benchmarks/thirdparty/benchpress/LICENSE b/benchmarks/thirdparty/benchpress/LICENSE
deleted file mode 100644
index 282efc46..00000000
--- a/benchmarks/thirdparty/benchpress/LICENSE
+++ /dev/null
@@ -1,21 +0,0 @@
-The MIT License (MIT)
-
-Copyright (c) 2014 Christopher Gilbert
-
-Permission is hereby granted, free of charge, to any person obtaining a copy
-of this software and associated documentation files (the "Software"), to deal
-in the Software without restriction, including without limitation the rights
-to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
-copies of the Software, and to permit persons to whom the Software is
-furnished to do so, subject to the following conditions:
-
-The above copyright notice and this permission notice shall be included in all
-copies or substantial portions of the Software.
-
-THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
-AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
-SOFTWARE.
\ No newline at end of file
diff --git a/benchmarks/thirdparty/benchpress/benchpress.hpp b/benchmarks/thirdparty/benchpress/benchpress.hpp
deleted file mode 100644
index d68e4aff..00000000
--- a/benchmarks/thirdparty/benchpress/benchpress.hpp
+++ /dev/null
@@ -1,401 +0,0 @@
-/*
-* Copyright (C) 2015 Christopher Gilbert.
-*
-* Permission is hereby granted, free of charge, to any person obtaining a copy
-* of this software and associated documentation files (the "Software"), to deal
-* in the Software without restriction, including without limitation the rights
-* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
-* copies of the Software, and to permit persons to whom the Software is
-* furnished to do so, subject to the following conditions:
-*
-* The above copyright notice and this permission notice shall be included in all
-* copies or substantial portions of the Software.
-*
-* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
-* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
-* SOFTWARE.
-*/
-#ifndef BENCHPRESS_HPP
-#define BENCHPRESS_HPP
-
-#include <algorithm>   // max, min
-#include <atomic>      // atomic_intmax_t
-#include <chrono>      // high_resolution_timer, duration
-#include <functional>  // function
-#include <iomanip>     // setw
-#include <iostream>    // cout
-#include <regex>       // regex, regex_match
-#include <sstream>     // stringstream
-#include <string>      // string
-#include <thread>      // thread
-#include <vector>      // vector
-
-namespace benchpress {
-
-/*
- * The options class encapsulates all options for running benchmarks.
- *
- * When including benchpress, a main function can be emitted which includes a command-line parser for building an
- * options object. However from time-to-time it may be necessary for the developer to have to build their own main
- * stub and construct the options object manually.
- *
- * options opts;
- * opts
- *     .bench(".*")
- *     .benchtime(1)
- *     .cpu(4);
- */
-class options {
-    std::string d_bench;
-    size_t      d_benchtime;
-    size_t      d_cpu;
-public:
-    options()
-        : d_bench(".*")
-        , d_benchtime(1)
-        , d_cpu(std::thread::hardware_concurrency())
-    {}
-    options& bench(const std::string& bench) {
-        d_bench = bench;
-        return *this;
-    }
-    options& benchtime(size_t benchtime) {
-        d_benchtime = benchtime;
-        return *this;
-    }
-    options& cpu(size_t cpu) {
-        d_cpu = cpu;
-        return *this;
-    }
-    std::string get_bench() const {
-        return d_bench;
-    }
-    size_t get_benchtime() const {
-        return d_benchtime;
-    }
-    size_t get_cpu() const {
-        return d_cpu;
-    }
-};
-
-class context;
-
-/*
- * The benchmark_info class is used to store a function name / pointer pair.
- *
- * benchmark_info bi("example", [](benchpress::context* b) {
- *     // benchmark function
- * });
- */
-class benchmark_info {
-    std::string                   d_name;
-    std::function<void(context*)> d_func;
-
-public:
-    benchmark_info(std::string name, std::function<void(context*)> func)
-        : d_name(name)
-        , d_func(func)
-    {}
-
-    std::string                   get_name() const { return d_name; }
-    std::function<void(context*)> get_func() const { return d_func; }
-};
-
-/*
- * The registration class is responsible for providing a single global point of reference for registering
- * benchmark functions.
- *
- * registration::get_ptr()->register_benchmark(info);
- */
-class registration {
-    static registration*        d_this;
-    std::vector<benchmark_info> d_benchmarks;
-
-public:
-    static registration* get_ptr() {
-        if (nullptr == d_this) {
-            d_this = new registration();
-        }
-        return d_this;
-    }
-
-    void register_benchmark(benchmark_info& info) {
-        d_benchmarks.push_back(info);
-    }
-
-    std::vector<benchmark_info> get_benchmarks() { return d_benchmarks; }
-};
-
-/*
- * The auto_register class is a helper used to register benchmarks.
- */
-class auto_register {
-public:
-    auto_register(const std::string& name, std::function<void(context*)> func) {
-        benchmark_info info(name, func);
-        registration::get_ptr()->register_benchmark(info);
-    }
-};
-
-#define CONCAT(x, y) x ## y
-#define CONCAT2(x, y) CONCAT(x, y)
-
-// The BENCHMARK macro is a helper for creating benchmark functions and automatically registering them with the
-// registration class.
-#define BENCHMARK(x, f) benchpress::auto_register CONCAT2(register_, __LINE__)((x), (f));
-
-// This macro will prevent the compiler from removing a redundant code path which has no side-effects.
-#define DISABLE_REDUNDANT_CODE_OPT() { asm(""); }
-
-/*
- * The result class is responsible for producing a printable string representation of a benchmark run.
- */
-class result {
-    size_t                   d_num_iterations;
-    std::chrono::nanoseconds d_duration;
-    size_t                   d_num_bytes;
-
-public:
-    result(size_t num_iterations, std::chrono::nanoseconds duration, size_t num_bytes)
-        : d_num_iterations(num_iterations)
-        , d_duration(duration)
-        , d_num_bytes(num_bytes)
-    {}
-
-    size_t get_ns_per_op() const {
-        if (d_num_iterations <= 0) {
-            return 0;
-        }
-        return d_duration.count() / d_num_iterations;
-    }
-
-    double get_mb_per_s() const {
-        if (d_num_iterations <= 0 || d_duration.count() <= 0 || d_num_bytes <= 0) {
-            return 0;
-        }
-        return ((double(d_num_bytes) * double(d_num_iterations) / double(1e6)) /
-                double(std::chrono::duration_cast<std::chrono::seconds>(d_duration).count()));
-    }
-
-    std::string to_string() const {
-        std::stringstream tmp;
-        tmp << std::setw(12) << std::right << d_num_iterations;
-        size_t npo = get_ns_per_op();
-        tmp << std::setw(12) << std::right << npo << std::setw(0) << " ns/op";
-        double mbs = get_mb_per_s();
-        if (mbs > 0.0) {
-            tmp << std::setw(12) << std::right << mbs << std::setw(0) << " MB/s";
-        }
-        return std::string(tmp.str());
-    }
-};
-
-/*
- * The parallel_context class is responsible for providing a thread-safe context for parallel benchmark code.
- */
-class parallel_context {
-    std::atomic_intmax_t d_num_iterations;
-public:
-    parallel_context(size_t num_iterations)
-        : d_num_iterations(num_iterations)
-    {}
-
-    bool next() {
-        return (d_num_iterations.fetch_sub(1) > 0);
-    }
-};
-
-/*
- * The context class is responsible for providing an interface for capturing benchmark metrics to benchmark functions.
- */
-class context {
-    bool                                           d_timer_on;
-    std::chrono::high_resolution_clock::time_point d_start;
-    std::chrono::nanoseconds                       d_duration;
-    std::chrono::seconds                           d_benchtime;
-    size_t                                         d_num_iterations;
-    size_t                                         d_num_threads;
-    size_t                                         d_num_bytes;
-    benchmark_info                                 d_benchmark;
-
-public:
-    context(const benchmark_info& info, const options& opts)
-        : d_timer_on(false)
-        , d_start()
-        , d_duration()
-        , d_benchtime(std::chrono::seconds(opts.get_benchtime()))
-        , d_num_iterations(1)
-        , d_num_threads(opts.get_cpu())
-        , d_num_bytes(0)
-        , d_benchmark(info)
-    {}
-
-    size_t num_iterations() const { return d_num_iterations; }
-
-    void set_num_threads(size_t n) { d_num_threads = n; }
-    size_t num_threads() const { return d_num_threads; }
-
-    void start_timer() {
-        if (!d_timer_on) {
-            d_start = std::chrono::high_resolution_clock::now();
-            d_timer_on = true;
-        }
-    }
-    void stop_timer() {
-        if (d_timer_on) {
-            d_duration += std::chrono::high_resolution_clock::now() - d_start;
-            d_timer_on = false;
-        }
-    }
-    void reset_timer() {
-        if (d_timer_on) {
-            d_start = std::chrono::high_resolution_clock::now();
-        }
-        d_duration = std::chrono::nanoseconds::zero();
-    }
-
-    void set_bytes(int64_t bytes) { d_num_bytes = bytes; }
-
-    size_t get_ns_per_op() {
-        if (d_num_iterations <= 0) {
-            return 0;
-        }
-        return d_duration.count() / d_num_iterations;
-    }
-
-    void run_n(size_t n) {
-        d_num_iterations = n;
-        reset_timer();
-        start_timer();
-        d_benchmark.get_func()(this);
-        stop_timer();
-    }
-
-    void run_parallel(std::function<void(parallel_context*)> f) {
-        parallel_context pc(d_num_iterations);
-        std::vector<std::thread> threads;
-        for (size_t i = 0; i < d_num_threads; ++i) {
-            threads.push_back(std::thread([&pc,&f]() -> void {
-                f(&pc);
-            }));
-        }
-        for(auto& thread : threads){
-            thread.join();
-        }
-    }
-
-    result run() {
-        size_t n = 1;
-        run_n(n);
-        while (d_duration < d_benchtime && n < 1e9) {
-            size_t last = n;
-            if (get_ns_per_op() == 0) {
-                n = 1e9;
-            } else {
-                n = d_duration.count() / get_ns_per_op();
-            }
-            n = std::max(std::min(n+n/2, 100*last), last+1);
-            n = round_up(n);
-            run_n(n);
-        }
-        return result(n, d_duration, d_num_bytes);
-    }
-
-private:
-    template<typename T>
-    T round_down_10(T n) {
-        int tens = 0;
-        while (n > 10) {
-            n /= 10;
-            tens++;
-        }
-        int result = 1;
-        for (int i = 0; i < tens; ++i) {
-            result *= 10;
-        }
-        return result;
-    }
-
-    template<typename T>
-    T round_up(T n) {
-        T base = round_down_10(n);
-        if (n < (2 * base)) {
-            return 2 * base;
-        }
-        if (n < (5 * base)) {
-            return 5 * base;
-        }
-        return 10 * base;
-    }
-};
-
-/*
- * The run_benchmarks function will run the registered benchmarks.
- */
-void run_benchmarks(const options& opts) {
-    std::regex match_r(opts.get_bench());
-    auto benchmarks = registration::get_ptr()->get_benchmarks();
-    for (auto& info : benchmarks) {
-        if (std::regex_match(info.get_name(), match_r)) {
-            context c(info, opts);
-            auto r = c.run();
-            std::cout << std::setw(35) << std::left << info.get_name() << r.to_string() << std::endl;
-        }
-    }
-}
-
-} // namespace benchpress
-
-/*
- * If BENCHPRESS_CONFIG_MAIN is defined when the file is included then a main function will be emitted which provides a
- * command-line parser and then executes run_benchmarks.
- */
-#ifdef BENCHPRESS_CONFIG_MAIN
-#include "cxxopts.hpp"
-benchpress::registration* benchpress::registration::d_this;
-int main(int argc, char** argv) {
-    std::chrono::high_resolution_clock::time_point bp_start = std::chrono::high_resolution_clock::now();
-    benchpress::options bench_opts;
-    try {
-        cxxopts::Options cmd_opts(argv[0], " - command line options");
-        cmd_opts.add_options()
-            ("bench", "run benchmarks matching the regular expression", cxxopts::value<std::string>()
-                ->default_value(".*"))
-            ("benchtime", "run enough iterations of each benchmark to take t seconds", cxxopts::value<size_t>()
-                ->default_value("1"))
-            ("cpu", "specify the number of threads to use for parallel benchmarks", cxxopts::value<size_t>()
-                ->default_value(std::to_string(std::thread::hardware_concurrency())))
-            ("help", "print help")
-        ;
-        cmd_opts.parse(argc, argv);
-        if (cmd_opts.count("help")) {
-            std::cout << cmd_opts.help({""}) << std::endl;
-            exit(0);
-        }
-        if (cmd_opts.count("bench")) {
-            bench_opts.bench(cmd_opts["bench"].as<std::string>());
-        }
-        if (cmd_opts.count("benchtime")) {
-            bench_opts.benchtime(cmd_opts["benchtime"].as<size_t>());
-        }
-        if (cmd_opts.count("cpu")) {
-            bench_opts.cpu(cmd_opts["cpu"].as<size_t>());
-        }
-    } catch (const cxxopts::OptionException& e) {
-        std::cout << "error parsing options: " << e.what() << std::endl;
-        exit(1);
-    }
-    benchpress::run_benchmarks(bench_opts);
-    float duration = std::chrono::duration_cast<std::chrono::milliseconds>(
-            std::chrono::high_resolution_clock::now() - bp_start
-    ).count() / 1000.f;
-    std::cout << argv[0] << " " << duration << "s" << std::endl;
-    return 0;
-}
-#endif
-
-#endif // BENCHPRESS_HPP
\ No newline at end of file
diff --git a/benchmarks/thirdparty/cxxopts/LICENSE b/benchmarks/thirdparty/cxxopts/LICENSE
deleted file mode 100644
index 324a2035..00000000
--- a/benchmarks/thirdparty/cxxopts/LICENSE
+++ /dev/null
@@ -1,19 +0,0 @@
-Copyright (c) 2014 Jarryd Beck
-
-Permission is hereby granted, free of charge, to any person obtaining a copy
-of this software and associated documentation files (the "Software"), to deal
-in the Software without restriction, including without limitation the rights
-to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
-copies of the Software, and to permit persons to whom the Software is
-furnished to do so, subject to the following conditions:
-
-The above copyright notice and this permission notice shall be included in
-all copies or substantial portions of the Software.
-
-THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
-AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
-THE SOFTWARE.
diff --git a/benchmarks/thirdparty/cxxopts/cxxopts.hpp b/benchmarks/thirdparty/cxxopts/cxxopts.hpp
deleted file mode 100644
index 94dd31ca..00000000
--- a/benchmarks/thirdparty/cxxopts/cxxopts.hpp
+++ /dev/null
@@ -1,1312 +0,0 @@
-/*
-
-Copyright (c) 2014 Jarryd Beck
-
-Permission is hereby granted, free of charge, to any person obtaining a copy
-of this software and associated documentation files (the "Software"), to deal
-in the Software without restriction, including without limitation the rights
-to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
-copies of the Software, and to permit persons to whom the Software is
-furnished to do so, subject to the following conditions:
-
-The above copyright notice and this permission notice shall be included in
-all copies or substantial portions of the Software.
-
-THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
-AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
-THE SOFTWARE.
-
-*/
-
-#ifndef CXX_OPTS_HPP
-#define CXX_OPTS_HPP
-
-#if defined(__GNUC__)
-#pragma GCC diagnostic push
-#pragma GCC diagnostic ignored "-Wnon-virtual-dtor"
-#endif
-
-#include <exception>
-#include <iostream>
-#include <map>
-#include <memory>
-#include <regex>
-#include <sstream>
-#include <string>
-#include <vector>
-
-//when we ask cxxopts to use Unicode, help strings are processed using ICU,
-//which results in the correct lengths being computed for strings when they
-//are formatted for the help output
-//it is necessary to make sure that <unicode/unistr.h> can be found by the
-//compiler, and that icu-uc is linked in to the binary.
-
-#ifdef CXXOPTS_USE_UNICODE
-#include <unicode/unistr.h>
-
-namespace cxxopts
-{
-  typedef icu::UnicodeString String;
-
-  inline
-  String
-  toLocalString(std::string s)
-  {
-    return icu::UnicodeString::fromUTF8(s);
-  }
-
-  class UnicodeStringIterator : public
-    std::iterator<std::forward_iterator_tag, int32_t>
-  {
-    public:
-
-    UnicodeStringIterator(const icu::UnicodeString* s, int32_t pos)
-    : s(s)
-    , i(pos)
-    {
-    }
-
-    value_type
-    operator*() const
-    {
-      return s->char32At(i);
-    }
-
-    bool
-    operator==(const UnicodeStringIterator& rhs) const
-    {
-      return s == rhs.s && i == rhs.i;
-    }
-
-    bool
-    operator!=(const UnicodeStringIterator& rhs) const
-    {
-      return !(*this == rhs);
-    }
-
-    UnicodeStringIterator&
-    operator++()
-    {
-      ++i;
-      return *this;
-    }
-
-    UnicodeStringIterator
-    operator+(int32_t v)
-    {
-      return UnicodeStringIterator(s, i + v);
-    }
-
-    private:
-    const icu::UnicodeString* s;
-    int32_t i;
-  };
-
-  inline
-  String&
-  stringAppend(String&s, String a)
-  {
-    return s.append(std::move(a));
-  }
-
-  inline
-  String&
-  stringAppend(String& s, int n, UChar32 c)
-  {
-    for (int i = 0; i != n; ++i)
-    {
-      s.append(c);
-    }
-
-    return s;
-  }
-
-  template <typename Iterator>
-  String&
-  stringAppend(String& s, Iterator begin, Iterator end)
-  {
-    while (begin != end)
-    {
-      s.append(*begin);
-      ++begin;
-    }
-
-    return s;
-  }
-
-  inline
-  size_t
-  stringLength(const String& s)
-  {
-    return s.length();
-  }
-
-  inline
-  std::string
-  toUTF8String(const String& s)
-  {
-    std::string result;
-    s.toUTF8String(result);
-
-    return result;
-  }
-}
-
-namespace std
-{
-  cxxopts::UnicodeStringIterator
-  begin(const icu::UnicodeString& s)
-  {
-    return cxxopts::UnicodeStringIterator(&s, 0);
-  }
-
-  cxxopts::UnicodeStringIterator
-  end(const icu::UnicodeString& s)
-  {
-    return cxxopts::UnicodeStringIterator(&s, s.length());
-  }
-}
-
-//ifdef CXXOPTS_USE_UNICODE
-#else
-
-namespace cxxopts
-{
-  typedef std::string String;
-
-  template <typename T>
-  T
-  toLocalString(T&& t)
-  {
-    return t;
-  }
-
-  inline
-  size_t
-  stringLength(const String& s)
-  {
-    return s.length();
-  }
-
-  inline
-  String&
-  stringAppend(String&s, String a)
-  {
-    return s.append(std::move(a));
-  }
-
-  inline
-  String&
-  stringAppend(String& s, size_t n, char c)
-  {
-    return s.append(n, c);
-  }
-
-  template <typename Iterator>
-  String&
-  stringAppend(String& s, Iterator begin, Iterator end)
-  {
-    return s.append(begin, end);
-  }
-
-  template <typename T>
-  std::string
-  toUTF8String(T&& t)
-  {
-    return std::forward<T>(t);
-  }
-
-}
-
-//ifdef CXXOPTS_USE_UNICODE
-#endif
-
-namespace cxxopts
-{
-  class Value : public std::enable_shared_from_this<Value>
-  {
-    public:
-
-    virtual void
-    parse(const std::string& text) const = 0;
-
-    virtual void
-    parse() const = 0;
-
-    virtual bool
-    has_arg() const = 0;
-
-    virtual bool
-    has_default() const = 0;
-
-    virtual bool
-    has_implicit() const = 0;
-
-    virtual std::string
-    get_default_value() const = 0;
-
-    virtual std::string
-    get_implicit_value() const = 0;
-
-    virtual std::shared_ptr<Value>
-    default_value(const std::string& value) = 0;
-
-    virtual std::shared_ptr<Value>
-    implicit_value(const std::string& value) = 0;
-  };
-
-  class OptionException : public std::exception
-  {
-    public:
-    OptionException(const std::string& message)
-    : m_message(message)
-    {
-    }
-
-    virtual const char*
-    what() const noexcept
-    {
-      return m_message.c_str();
-    }
-
-    private:
-    std::string m_message;
-  };
-
-  class OptionSpecException : public OptionException
-  {
-    public:
-
-    OptionSpecException(const std::string& message)
-    : OptionException(message)
-    {
-    }
-  };
-
-  class OptionParseException : public OptionException
-  {
-    public:
-    OptionParseException(const std::string& message)
-    : OptionException(message)
-    {
-    }
-  };
-
-  class option_exists_error : public OptionSpecException
-  {
-    public:
-    option_exists_error(const std::string& option)
-    : OptionSpecException(u8"Option ‘" + option + u8"’ already exists")
-    {
-    }
-  };
-
-  class invalid_option_format_error : public OptionSpecException
-  {
-    public:
-    invalid_option_format_error(const std::string& format)
-    : OptionSpecException(u8"Invalid option format ‘" + format + u8"’")
-    {
-    }
-  };
-
-  class option_not_exists_exception : public OptionParseException
-  {
-    public:
-    option_not_exists_exception(const std::string& option)
-    : OptionParseException(u8"Option ‘" + option + u8"’ does not exist")
-    {
-    }
-  };
-
-  class missing_argument_exception : public OptionParseException
-  {
-    public:
-    missing_argument_exception(const std::string& option)
-    : OptionParseException(u8"Option ‘" + option + u8"’ is missing an argument")
-    {
-    }
-  };
-
-  class option_requires_argument_exception : public OptionParseException
-  {
-    public:
-    option_requires_argument_exception(const std::string& option)
-    : OptionParseException(u8"Option ‘" + option + u8"’ requires an argument")
-    {
-    }
-  };
-
-  class option_not_has_argument_exception : public OptionParseException
-  {
-    public:
-    option_not_has_argument_exception
-    (
-      const std::string& option,
-      const std::string& arg
-    )
-    : OptionParseException(
-        u8"Option ‘" + option + u8"’ does not take an argument, but argument‘"
-        + arg + "’ given")
-    {
-    }
-  };
-
-  class option_not_present_exception : public OptionParseException
-  {
-    public:
-    option_not_present_exception(const std::string& option)
-    : OptionParseException(u8"Option ‘" + option + u8"’ not present")
-    {
-    }
-  };
-
-  class argument_incorrect_type : public OptionParseException
-  {
-    public:
-    argument_incorrect_type
-    (
-      const std::string& arg
-    )
-    : OptionParseException(
-      u8"Argument ‘" + arg + u8"’ failed to parse"
-    )
-    {
-    }
-  };
-
-  namespace values
-  {
-    template <typename T>
-    void
-    parse_value(const std::string& text, T& value)
-    {
-      std::istringstream is(text);
-      if (!(is >> value))
-      {
-        std::cerr << "cannot parse empty value" << std::endl;
-        throw argument_incorrect_type(text);
-      }
-
-      if (is.rdbuf()->in_avail() != 0)
-      {
-        throw argument_incorrect_type(text);
-      }
-    }
-
-    template <typename T>
-    void
-    parse_value(const std::string& text, std::vector<T>& value)
-    {
-      T v;
-      parse_value(text, v);
-      value.push_back(v);
-    }
-
-    inline
-    void
-    parse_value(const std::string& /*text*/, bool& value)
-    {
-      //TODO recognise on, off, yes, no, enable, disable
-      //so that we can write --long=yes explicitly
-      value = true;
-    }
-
-    inline
-    void
-    parse_value(const std::string& text, std::string& value)
-    {
-      value = text;
-    }
-
-    template <typename T>
-    struct value_has_arg
-    {
-      static constexpr bool value = true;
-    };
-
-    template <>
-    struct value_has_arg<bool>
-    {
-      static constexpr bool value = false;
-    };
-
-    template <typename T>
-    class standard_value : public Value
-    {
-      public:
-      standard_value()
-      : m_result(std::make_shared<T>())
-      , m_store(m_result.get())
-      {
-      }
-
-      standard_value(T* t)
-      : m_store(t)
-      {
-      }
-
-      void
-      parse(const std::string& text) const
-      {
-        if (m_implicit && text.empty())
-        {
-          parse_value(m_implicit_value, *m_store);
-        }
-        else
-        {
-          parse_value(text, *m_store);
-        }
-      }
-
-      void
-      parse() const
-      {
-        parse_value(m_default_value, *m_store);
-      }
-
-      bool
-      has_arg() const
-      {
-        return value_has_arg<T>::value;
-      }
-
-      bool
-      has_default() const
-      {
-        return m_default;
-      }
-
-      bool
-      has_implicit() const
-      {
-        return m_implicit;
-      }
-
-      virtual std::shared_ptr<Value>
-      default_value(const std::string& value){
-        m_default = true;
-        m_default_value = value;
-        return shared_from_this();
-      }
-
-      virtual std::shared_ptr<Value>
-      implicit_value(const std::string& value){
-        m_implicit = true;
-        m_implicit_value = value;
-        return shared_from_this();
-      }
-
-      std::string
-      get_default_value() const
-      {
-        return m_default_value;
-      }
-
-      std::string
-      get_implicit_value() const
-      {
-        return m_implicit_value;
-      }
-
-      const T&
-      get() const
-      {
-        if (m_store == nullptr)
-        {
-          return *m_result;
-        }
-        else
-        {
-          return *m_store;
-        }
-      }
-
-      protected:
-      std::shared_ptr<T> m_result;
-      T* m_store;
-      bool m_default = false;
-      std::string m_default_value;
-      bool m_implicit = false;
-      std::string m_implicit_value;
-    };
-  }
-
-  template <typename T>
-  std::shared_ptr<Value>
-  value()
-  {
-    return std::make_shared<values::standard_value<T>>();
-  }
-
-  template <typename T>
-  std::shared_ptr<Value>
-  value(T& t)
-  {
-    return std::make_shared<values::standard_value<T>>(&t);
-  }
-
-  class OptionAdder;
-
-  class OptionDetails
-  {
-    public:
-    OptionDetails
-    (
-      const String& description,
-      std::shared_ptr<const Value> value
-    )
-    : m_desc(description)
-    , m_value(value)
-    , m_count(0)
-    {
-    }
-
-    const String&
-    description() const
-    {
-      return m_desc;
-    }
-
-    bool
-    has_arg() const
-    {
-      return m_value->has_arg();
-    }
-
-    void
-    parse(const std::string& text)
-    {
-      m_value->parse(text);
-      ++m_count;
-    }
-
-    void
-    parse_default()
-    {
-      m_value->parse();
-      ++m_count;
-    }
-
-    int
-    count() const
-    {
-      return m_count;
-    }
-
-    const Value& value() const {
-        return *m_value;
-    }
-
-    template <typename T>
-    const T&
-    as() const
-    {
-#ifdef CXXOPTS_NO_RTTI
-      return static_cast<const values::standard_value<T>&>(*m_value).get();
-#else
-      return dynamic_cast<const values::standard_value<T>&>(*m_value).get();
-#endif
-    }
-
-    private:
-    String m_desc;
-    std::shared_ptr<const Value> m_value;
-    int m_count;
-  };
-
-  struct HelpOptionDetails
-  {
-    std::string s;
-    std::string l;
-    String desc;
-    bool has_arg;
-    bool has_default;
-    std::string default_value;
-    bool has_implicit;
-    std::string implicit_value;
-    std::string arg_help;
-  };
-
-  struct HelpGroupDetails
-  {
-    std::string name;
-    std::string description;
-    std::vector<HelpOptionDetails> options;
-  };
-
-  class Options
-  {
-    public:
-
-    Options(std::string program, std::string help_string = "")
-    : m_program(std::move(program))
-    , m_help_string(toLocalString(std::move(help_string)))
-    {
-    }
-
-    inline
-    void
-    parse(int& argc, char**& argv);
-
-    inline
-    OptionAdder
-    add_options(std::string group = "");
-
-    inline
-    void
-    add_option
-    (
-      const std::string& group,
-      const std::string& s,
-      const std::string& l,
-      std::string desc,
-      std::shared_ptr<const Value> value,
-      std::string arg_help
-    );
-
-    int
-    count(const std::string& o) const
-    {
-      auto iter = m_options.find(o);
-      if (iter == m_options.end())
-      {
-        return 0;
-      }
-
-      return iter->second->count();
-    }
-
-    const OptionDetails&
-    operator[](const std::string& option) const
-    {
-      auto iter = m_options.find(option);
-
-      if (iter == m_options.end())
-      {
-        throw option_not_present_exception(option);
-      }
-
-      return *iter->second;
-    }
-
-    //parse positional arguments into the given option
-    inline
-    void
-    parse_positional(std::string option);
-
-    inline
-    std::string
-    help(const std::vector<std::string>& groups = {""}) const;
-
-    inline
-    const std::vector<std::string>
-    groups() const;
-
-    inline
-    const HelpGroupDetails&
-    group_help(const std::string& group) const;
-
-    private:
-
-    inline
-    void
-    add_one_option
-    (
-      const std::string& option,
-      std::shared_ptr<OptionDetails> details
-    );
-
-    inline
-    bool
-    consume_positional(std::string a);
-
-    inline
-    void
-    add_to_option(const std::string& option, const std::string& arg);
-
-    inline
-    void
-    parse_option
-    (
-      std::shared_ptr<OptionDetails> value,
-      const std::string& name,
-      const std::string& arg = ""
-    );
-
-    inline
-    void
-    checked_parse_arg
-    (
-      int argc,
-      char* argv[],
-      int& current,
-      std::shared_ptr<OptionDetails> value,
-      const std::string& name
-    );
-
-    inline
-    String
-    help_one_group(const std::string& group) const;
-
-    std::string m_program;
-    String m_help_string;
-
-    std::map<std::string, std::shared_ptr<OptionDetails>> m_options;
-    std::string m_positional;
-
-    //mapping from groups to help options
-    std::map<std::string, HelpGroupDetails> m_help;
-  };
-
-  class OptionAdder
-  {
-    public:
-
-    OptionAdder(Options& options, std::string group)
-    : m_options(options), m_group(std::move(group))
-    {
-    }
-
-    inline
-    OptionAdder&
-    operator()
-    (
-      const std::string& opts,
-      const std::string& desc,
-      std::shared_ptr<const Value> value
-        = ::cxxopts::value<bool>(),
-      std::string arg_help = ""
-    );
-
-    private:
-    Options& m_options;
-    std::string m_group;
-  };
-
-}
-
-namespace cxxopts
-{
-
-  namespace
-  {
-
-    constexpr int OPTION_LONGEST = 30;
-    constexpr int OPTION_DESC_GAP = 2;
-
-    std::basic_regex<char> option_matcher
-      ("--([[:alnum:]][-_[:alnum:]]+)(=(.*))?|-([a-zA-Z]+)");
-
-    std::basic_regex<char> option_specifier
-      ("(([a-zA-Z]),)?([a-zA-Z0-9][-_a-zA-Z0-9]+)");
-
-    String
-    format_option
-    (
-      const HelpOptionDetails& o
-    )
-    {
-      auto& s = o.s;
-      auto& l = o.l;
-
-      String result = "  ";
-
-      if (s.size() > 0)
-      {
-        result += "-" + toLocalString(s) + ",";
-      }
-      else
-      {
-        result += "   ";
-      }
-
-      if (l.size() > 0)
-      {
-        result += " --" + toLocalString(l);
-      }
-
-      if (o.has_arg)
-      {
-        auto arg = o.arg_help.size() > 0 ? toLocalString(o.arg_help) : "arg";
-
-        if (o.has_implicit)
-        {
-          result += " [=" + arg + "(=" + toLocalString(o.implicit_value) + ")]";
-        }
-        else
-        {
-          result += " " + arg;
-        }
-      }
-
-      return result;
-    }
-
-    String
-    format_description
-    (
-      const HelpOptionDetails& o,
-      size_t start,
-      size_t width
-    )
-    {
-      auto desc = o.desc;
-
-      if (o.has_default)
-      {
-        desc += toLocalString(" (default:" + o.default_value + ")");
-      }
-
-      String result;
-
-      auto current = std::begin(desc);
-      auto startLine = current;
-      auto lastSpace = current;
-
-      auto size = size_t{};
-
-      while (current != std::end(desc))
-      {
-        if (*current == ' ')
-        {
-          lastSpace = current;
-        }
-
-        if (size > width)
-        {
-          if (lastSpace == startLine)
-          {
-            stringAppend(result, startLine, current + 1);
-            stringAppend(result, "\n");
-            stringAppend(result, start, ' ');
-            startLine = current + 1;
-            lastSpace = startLine;
-          }
-          else
-          {
-            stringAppend(result, startLine, lastSpace);
-            stringAppend(result, "\n");
-            stringAppend(result, start, ' ');
-            startLine = lastSpace + 1;
-          }
-          size = 0;
-        }
-        else
-        {
-          ++size;
-        }
-
-        ++current;
-      }
-
-      //append whatever is left
-      stringAppend(result, startLine, current);
-
-      return result;
-    }
-  }
-
-OptionAdder
-Options::add_options(std::string group)
-{
-  return OptionAdder(*this, std::move(group));
-}
-
-OptionAdder&
-OptionAdder::operator()
-(
-  const std::string& opts,
-  const std::string& desc,
-  std::shared_ptr<const Value> value,
-  std::string arg_help
-)
-{
-  std::match_results<const char*> result;
-  std::regex_match(opts.c_str(), result, option_specifier);
-
-  if (result.empty())
-  {
-    throw invalid_option_format_error(opts);
-  }
-
-  const auto& s = result[2];
-  const auto& l = result[3];
-
-  m_options.add_option(m_group, s.str(), l.str(), desc, value,
-    std::move(arg_help));
-
-  return *this;
-}
-
-void
-Options::parse_option
-(
-  std::shared_ptr<OptionDetails> value,
-  const std::string& /*name*/,
-  const std::string& arg
-)
-{
-  value->parse(arg);
-}
-
-void
-Options::checked_parse_arg
-(
-  int argc,
-  char* argv[],
-  int& current,
-  std::shared_ptr<OptionDetails> value,
-  const std::string& name
-)
-{
-  if (current + 1 >= argc)
-  {
-    if (value->value().has_implicit())
-    {
-      parse_option(value, name, "");
-    }
-    else
-    {
-      throw missing_argument_exception(name);
-    }
-  }
-  else
-  {
-    if (argv[current + 1][0] == '-' && value->value().has_implicit())
-    {
-      parse_option(value, name, "");
-    }
-    else
-    {
-      parse_option(value, name, argv[current + 1]);
-      ++current;
-    }
-  }
-}
-
-void
-Options::add_to_option(const std::string& option, const std::string& arg)
-{
-  auto iter = m_options.find(option);
-
-  if (iter == m_options.end())
-  {
-    throw option_not_exists_exception(option);
-  }
-
-  parse_option(iter->second, option, arg);
-}
-
-bool
-Options::consume_positional(std::string a)
-{
-  if (m_positional.size() > 0)
-  {
-    add_to_option(m_positional, a);
-    return true;
-  }
-  else
-  {
-    return false;
-  }
-}
-
-void
-Options::parse_positional(std::string option)
-{
-  m_positional = std::move(option);
-}
-
-void
-Options::parse(int& argc, char**& argv)
-{
-  int current = 1;
-
-  int nextKeep = 1;
-
-  while (current != argc)
-  {
-    std::match_results<const char*> result;
-    std::regex_match(argv[current], result, option_matcher);
-
-    if (result.empty())
-    {
-      //not a flag
-
-      //if true is returned here then it was consumed, otherwise it is
-      //ignored
-      if (consume_positional(argv[current]))
-      {
-      }
-      else
-      {
-        argv[nextKeep] = argv[current];
-        ++nextKeep;
-      }
-      //if we return from here then it was parsed successfully, so continue
-    }
-    else
-    {
-      //short or long option?
-      if (result[4].length() != 0)
-      {
-        const std::string& s = result[4];
-
-        for (std::size_t i = 0; i != s.size(); ++i)
-        {
-          std::string name(1, s[i]);
-          auto iter = m_options.find(name);
-
-          if (iter == m_options.end())
-          {
-            throw option_not_exists_exception(name);
-          }
-
-          auto value = iter->second;
-
-          //if no argument then just add it
-          if (!value->has_arg())
-          {
-            parse_option(value, name);
-          }
-          else
-          {
-            //it must be the last argument
-            if (i + 1 == s.size())
-            {
-              checked_parse_arg(argc, argv, current, value, name);
-            }
-            else if (value->value().has_implicit())
-            {
-              parse_option(value, name, "");
-            }
-            else
-            {
-              //error
-              throw option_requires_argument_exception(name);
-            }
-          }
-        }
-      }
-      else if (result[1].length() != 0)
-      {
-        const std::string& name = result[1];
-
-        auto iter = m_options.find(name);
-
-        if (iter == m_options.end())
-        {
-          throw option_not_exists_exception(name);
-        }
-
-        auto opt = iter->second;
-
-        //equals provided for long option?
-        if (result[3].length() != 0)
-        {
-          //parse the option given
-
-          //but if it doesn't take an argument, this is an error
-          if (!opt->has_arg())
-          {
-            throw option_not_has_argument_exception(name, result[3]);
-          }
-
-          parse_option(opt, name, result[3]);
-        }
-        else
-        {
-          if (opt->has_arg())
-          {
-            //parse the next argument
-            checked_parse_arg(argc, argv, current, opt, name);
-          }
-          else
-          {
-            //parse with empty argument
-            parse_option(opt, name);
-          }
-        }
-      }
-
-    }
-
-    ++current;
-  }
-
-  for (auto& opt : m_options)
-  {
-    auto& detail = opt.second;
-    auto& value = detail->value();
-
-    if(!detail->count() && value.has_default()){
-      detail->parse_default();
-    }
-  }
-
-  argc = nextKeep;
-}
-
-void
-Options::add_option
-(
-  const std::string& group,
-  const std::string& s,
-  const std::string& l,
-  std::string desc,
-  std::shared_ptr<const Value> value,
-  std::string arg_help
-)
-{
-  auto stringDesc = toLocalString(std::move(desc));
-  auto option = std::make_shared<OptionDetails>(stringDesc, value);
-
-  if (s.size() > 0)
-  {
-    add_one_option(s, option);
-  }
-
-  if (l.size() > 0)
-  {
-    add_one_option(l, option);
-  }
-
-  //add the help details
-  auto& options = m_help[group];
-
-  options.options.emplace_back(HelpOptionDetails{s, l, stringDesc,
-      value->has_arg(),
-      value->has_default(), value->get_default_value(),
-      value->has_implicit(), value->get_implicit_value(),
-      std::move(arg_help)});
-}
-
-void
-Options::add_one_option
-(
-  const std::string& option,
-  std::shared_ptr<OptionDetails> details
-)
-{
-  auto in = m_options.emplace(option, details);
-
-  if (!in.second)
-  {
-    throw option_exists_error(option);
-  }
-}
-
-String
-Options::help_one_group(const std::string& g) const
-{
-  typedef std::vector<std::pair<String, String>> OptionHelp;
-
-  auto group = m_help.find(g);
-  if (group == m_help.end())
-  {
-    return "";
-  }
-
-  OptionHelp format;
-
-  size_t longest = 0;
-
-  String result;
-
-  if (!g.empty())
-  {
-    result += toLocalString(" " + g + " options:\n\n");
-  }
-
-  for (const auto& o : group->second.options)
-  {
-    auto s = format_option(o);
-    longest = std::max(longest, stringLength(s));
-    format.push_back(std::make_pair(s, String()));
-  }
-
-  longest = std::min(longest, static_cast<size_t>(OPTION_LONGEST));
-
-  //widest allowed description
-  auto allowed = size_t{76} - longest - OPTION_DESC_GAP;
-
-  auto fiter = format.begin();
-  for (const auto& o : group->second.options)
-  {
-    auto d = format_description(o, longest + OPTION_DESC_GAP, allowed);
-
-    result += fiter->first;
-    if (stringLength(fiter->first) > longest)
-    {
-      result += "\n";
-      result += toLocalString(std::string(longest + OPTION_DESC_GAP, ' '));
-    }
-    else
-    {
-      result += toLocalString(std::string(longest + OPTION_DESC_GAP -
-        stringLength(fiter->first),
-        ' '));
-    }
-    result += d;
-    result += "\n";
-
-    ++fiter;
-  }
-
-  return result;
-}
-
-std::string
-Options::help(const std::vector<std::string>& groups) const
-{
-  String result = "Usage:\n  " + toLocalString(m_program) + " [OPTION...]"
-    + m_help_string + "\n\n";
-
-  for (std::size_t i = 0; i < groups.size(); ++i)
-  {
-    result += help_one_group(groups[i]);
-    if (i < groups.size() - 1)
-    {
-      result += "\n";
-    }
-  }
-
-  return toUTF8String(result);
-}
-
-const std::vector<std::string>
-Options::groups() const
-{
-  std::vector<std::string> g;
-
-  std::transform(
-    m_help.begin(),
-    m_help.end(),
-    std::back_inserter(g),
-    [] (const std::map<std::string, HelpGroupDetails>::value_type& pair)
-    {
-      return pair.first;
-    }
-  );
-
-  return g;
-}
-
-const HelpGroupDetails&
-Options::group_help(const std::string& group) const
-{
-  return m_help.at(group);
-}
-
-}
-
-#if defined(__GNU__)
-#pragma GCC diagnostic pop
-#endif
-
-#endif //CXX_OPTS_HPP
\ No newline at end of file
diff --git a/develop/json.hpp b/develop/json.hpp
index 77888236..b2bb49c8 100644
--- a/develop/json.hpp
+++ b/develop/json.hpp
@@ -7247,7 +7247,7 @@ class basic_json
             // wrapper to get a value for an operation
             const auto get_value = [&val](const std::string & op,
                                           const std::string & member,
-                                          bool string_type) -> basic_json&
+                                          bool string_type) -> basic_json &
             {
                 // find value
                 auto it = val.m_value.object->find(member);
diff --git a/src/json.hpp b/src/json.hpp
index 2abea0fd..dafc1082 100644
--- a/src/json.hpp
+++ b/src/json.hpp
@@ -14535,7 +14535,7 @@ class basic_json
             // wrapper to get a value for an operation
             const auto get_value = [&val](const std::string & op,
                                           const std::string & member,
-                                          bool string_type) -> basic_json&
+                                          bool string_type) -> basic_json &
             {
                 // find value
                 auto it = val.m_value.object->find(member);
diff --git a/test/src/unit-inspection.cpp b/test/src/unit-inspection.cpp
index 0043a9ec..21f637aa 100644
--- a/test/src/unit-inspection.cpp
+++ b/test/src/unit-inspection.cpp
@@ -316,8 +316,8 @@ TEST_CASE("object inspection")
     SECTION("round trips")
     {
         for (const auto& s :
-    {"3.141592653589793", "1000000000000000010E5"
-    })
+                {"3.141592653589793", "1000000000000000010E5"
+                })
         {
             json j1 = json::parse(s);
             std::string s1 = j1.dump();