Merge branch 'release/2.0.0'
diff --git a/.github/CONTRIBUTING.md b/.github/CONTRIBUTING.md
index ea57e65..66420e9 100644
--- a/.github/CONTRIBUTING.md
+++ b/.github/CONTRIBUTING.md
@@ -35,7 +35,7 @@
    To run [`re2c`](http://re2c.org) and generate/overwrite file `src/json.hpp` with your changes in file `src/json.hpp.re2c`.
 
 
-2. [`test/unit.cpp`](https://github.com/nlohmann/json/blob/master/test/unit.cpp) - This contains the [Catch](https://github.com/philsquared/Catch) unit tests which currently cover [100 %](https://coveralls.io/github/nlohmann/json) of the library's code.
+2. [`test/src/unit.cpp`](https://github.com/nlohmann/json/blob/master/test/unit.cpp) - This contains the [Catch](https://github.com/philsquared/Catch) unit tests which currently cover [100 %](https://coveralls.io/github/nlohmann/json) of the library's code.
 
    If you add or change a feature, please also add a unit test to this file. The unit tests can be compiled with
 
@@ -70,9 +70,6 @@
 
 The following areas really need contribution:
 
-- Getting the code to compile without errors with the latest **Microsoft Visual Studio** version. I am not using Windows, so I cannot debug code with MSVC myself. There is a job on [AppVeyor](https://ci.appveyor.com/project/nlohmann/json) though.
-- Extending the **continuous integration** beyond Linux running some versions of GCC and Clang on [Travis](https://travis-ci.org/nlohmann/json) and Microsoft Visual Studio on [AppVeyor](https://ci.appveyor.com/project/nlohmann/json). We have found a lot of bugs just because several compilers behave in a slightly different manner.
+- Extending the **continuous integration** toward more exotic compilers such as Android NDK, Intel's Compiler, or the bleeding-edge versions of GCC or Clang.
 - Improving the efficiency of the **JSON parser**. The current parser is implemented as a naive recursive descent parser with hand coded string handling. More sophisticated approaches like LALR parsers would be really appreciated. That said, parser generators like Bison or ANTLR do not play nice with single-header files -- I really would like to keep the parser inside the `json.hpp` header, and I am not aware of approaches similar to [`re2c`](http://re2c.org) for parsing.
 - Extending and updating existing **benchmarks** to include (the most recent version of) this library. Though efficiency is not everything, speed and memory consumption are very important characteristics for C++ developers, so having proper comparisons would be interesting.
-- Check the code with [**Coverity**](https://scan.coverity.com).
-- Make the code **locale-independent**. The library currently only works for a locale where `.` is the decimal point character.
diff --git a/.github/PULL_REQUEST_TEMPLATE.md b/.github/PULL_REQUEST_TEMPLATE.md
index 2792058..49d1665 100644
--- a/.github/PULL_REQUEST_TEMPLATE.md
+++ b/.github/PULL_REQUEST_TEMPLATE.md
@@ -14,7 +14,7 @@
    To run [`re2c`](http://re2c.org) and generate/overwrite file `src/json.hpp` with your changes in file `src/json.hpp.re2c`.
 
 
-2. [`test/unit.cpp`](https://github.com/nlohmann/json/blob/master/test/unit.cpp) - This contains the [Catch](https://github.com/philsquared/Catch) unit tests which currently cover [100 %](https://coveralls.io/github/nlohmann/json) of the library's code.
+2. [`test/src/unit.cpp`](https://github.com/nlohmann/json/blob/master/test/unit.cpp) - This contains the [Catch](https://github.com/philsquared/Catch) unit tests which currently cover [100 %](https://coveralls.io/github/nlohmann/json) of the library's code.
 
    If you add or change a feature, please also add a unit test to this file. The unit tests can be compiled with
 
diff --git a/.gitignore b/.gitignore
index e1011fc..d5bd2f7 100644
--- a/.gitignore
+++ b/.gitignore
@@ -9,3 +9,6 @@
 
 html
 me.nlohmann.json.docset
+
+android
+doc/xml
diff --git a/.travis.yml b/.travis.yml
index 629bec8..b459f5d 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -3,6 +3,12 @@
 dist: trusty
 sudo: required
 
+env:
+  global:
+   # The next declaration is the encrypted COVERITY_SCAN_TOKEN, created
+   #   via the "travis encrypt" command using the project repo's public key
+   - secure: "m89SSgE+ASLO38rSKx7MTXK3n5NkP9bIx95jwY71YEiuFzib30PDJ/DifKnXxBjvy/AkCGztErQRk/8ZCvq+4HXozU2knEGnL/RUitvlwbhzfh2D4lmS3BvWBGS3N3NewoPBrRmdcvnT0xjOGXxtZaJ3P74TkB9GBnlz/HmKORA="
+
 # from http://stackoverflow.com/a/32127147/266378
 matrix:
   include:
@@ -19,49 +25,146 @@
         - touch src/json.hpp
         - make json_unit CXXFLAGS="-fprofile-arcs -ftest-coverage -std=c++11 -lstdc++" CXX=$COMPILER
         - ./json_unit "*"
-        - coveralls --exclude test/catch.hpp --exclude test/unit.cpp --include src/json.hpp --gcov-options '\-lp' --gcov 'gcov-4.9'
+        - coveralls --exclude test/src/catch.hpp --exclude test/src/unit.cpp --include src/json.hpp --gcov-options '\-lp' --gcov 'gcov-4.9'
+        - bash <(curl -s https://codecov.io/bash)
       env: COMPILER=g++-4.9
 
     - os: linux
       compiler: gcc
+      before_install: echo -n | openssl s_client -connect scan.coverity.com:443 | sed -ne '/-BEGIN CERTIFICATE-/,/-END CERTIFICATE-/p' | sudo tee -a /etc/ssl/certs/ca-certificates.crt
+      addons:
+        apt:
+          sources: ['ubuntu-toolchain-r-test']
+          packages: ['g++-5', 'valgrind']
+        coverity_scan:
+          project:
+            name: "nlohmann/json"
+            description: "Build submitted via Travis CI"
+          notification_email: niels.lohmann@gmail.com
+          build_command_prepend: "make clean ; sudo cp $(which g++-5) $(which g++)"
+          build_command: "make"
+          branch_pattern: coverity_scan
+      env: COMPILER=g++-5
+
+    - os: linux
+      compiler: gcc
       addons:
         apt:
           sources: ['ubuntu-toolchain-r-test']
-          packages: ['g++-5', 'valgrind']
-      env: COMPILER=g++-5
+          packages: ['g++-6', 'valgrind']
+      env: COMPILER=g++-6
 
+    # from https://github.com/travis-ci/travis-ci/issues/6120
     - os: linux
-      compiler: clang
-      addons:
-        apt:
-          sources: ['ubuntu-toolchain-r-test', 'llvm-toolchain-precise-3.6']
-          packages: ['clang-3.6', 'valgrind']
-      env: COMPILER=clang++-3.6
+      env:
+        - LLVM_VERSION=3.8.0
+        - LLVM_ARCHIVE_PATH=$HOME/clang+llvm.tar.xz
+        - COMPILER=clang++
+        - CPPFLAGS="-I $HOME/clang-$LLVM_VERSION/include/c++/v1"
+        - CXXFLAGS=-lc++
+        - PATH=$HOME/clang-$LLVM_VERSION/bin:$PATH
+        - LD_LIBRARY_PATH=$HOME/clang-$LLVM_VERSION/lib:$LD_LIBRARY_PATH
+      before_install:
+       - wget http://llvm.org/releases/$LLVM_VERSION/clang+llvm-$LLVM_VERSION-x86_64-linux-gnu-ubuntu-14.04.tar.xz -O $LLVM_ARCHIVE_PATH
+       - mkdir $HOME/clang-$LLVM_VERSION
+       - tar xf $LLVM_ARCHIVE_PATH -C $HOME/clang-$LLVM_VERSION --strip-components 1
 
-    - os: linux
+    # Clang 3.5 is not able to compile the code,
+    # see https://travis-ci.org/nlohmann/json/jobs/126720186
+
+#    - os: linux
+#      compiler: clang
+#      addons:
+#        apt:
+#          sources: ['ubuntu-toolchain-r-test', 'llvm-toolchain-precise-3.6']
+#          packages: ['clang-3.6', 'valgrind']
+#      env: COMPILER=clang++-3.6
+#
+#    - os: linux
+#      compiler: clang
+#      addons:
+#        apt:
+#          sources: ['ubuntu-toolchain-r-test', 'llvm-toolchain-precise-3.7']
+#          packages: ['clang-3.7', 'valgrind']
+#      env: COMPILER=clang++-3.7
+#
+#    - os: linux
+#      compiler: clang
+#      addons:
+#        apt:
+#          sources: ['ubuntu-toolchain-r-test', 'llvm-toolchain-precise-3.8']
+#          packages: ['clang-3.8', 'valgrind']
+#      env: COMPILER=clang++-3.8
+
+  #  - os: linux
+  #    compiler: clang
+  #    addons:
+  #      apt:
+  #        sources: ['ubuntu-toolchain-r-test', 'llvm-toolchain-precise']
+  #        packages: ['clang-3.9', 'valgrind']
+  #    env: COMPILER=clang++-3.9
+
+    - os: osx
+      osx_image: beta-xcode6.1
       compiler: clang
-      addons:
-        apt:
-          sources: ['ubuntu-toolchain-r-test', 'llvm-toolchain-precise-3.7']
-          packages: ['clang-3.7', 'valgrind']
-      env: COMPILER=clang++-3.7
+      env:
+       - COMPILER=clang
+       - CXXFLAGS=-lstdc++
+
+    - os: osx
+      osx_image: beta-xcode6.2
+      compiler: clang
+      env:
+       - COMPILER=clang
+       - CXXFLAGS=-lstdc++
+
+    - os: osx
+      osx_image: beta-xcode6.3
+      compiler: clang
+      env:
+       - COMPILER=clang
+       - CXXFLAGS=-lstdc++
+
+    - os: osx
+      osx_image: xcode6.4
+      compiler: clang
+      env:
+       - COMPILER=clang
+       - CXXFLAGS=-lstdc++
+
+    - os: osx
+      osx_image: xcode7.1
+      compiler: clang
+      env:
+       - COMPILER=clang
+       - CXXFLAGS=-lstdc++
      
-   # - os: osx
-   #   compiler: clang
-   #   env: COMPILER=clang
-   #   before_install:
-   #     - brew update
-   #     - brew install valgrind
+    - os: osx
+      osx_image: xcode7.2
+      compiler: clang
+      env:
+       - COMPILER=clang
+       - CXXFLAGS=-lstdc++
+
+    - os: osx
+      osx_image: xcode7.3
+      compiler: clang
+      env:
+       - COMPILER=clang
+       - CXXFLAGS=-lstdc++
+
+    - os: osx
+      osx_image: xcode8
+      compiler: clang
+      env:
+       - COMPILER=clang
+       - CXXFLAGS=-lstdc++
 
 script:
-  - make CXX=$COMPILER CXXFLAGS="-lstdc++"
+  - uname -a
+  - $COMPILER --version
+  - make CXX=$COMPILER
   - ./json_unit "*"
-  - valgrind --error-exitcode=1 --leak-check=full ./json_unit
-
-notifications:
-  webhooks:
-    urls:
-      - https://webhooks.gitter.im/e/f1196addb0e97a5ff396
-    on_success: change
-    on_failure: always
-    on_start: never
+  - if [ `which valgrind` ]; then
+    valgrind --error-exitcode=1 --leak-check=full ./json_unit ;
+    fi
diff --git a/CMakeLists.txt b/CMakeLists.txt
index 16e9f96..c0488b3 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -1,25 +1,54 @@
-cmake_minimum_required(VERSION 2.8)
+cmake_minimum_required(VERSION 3.0)
 
-project(json CXX)
+# define the project
+project(nlohmann_json VERSION 2.0.0 LANGUAGES CXX)
 
-add_executable(json_unit
-    src/json.hpp test/catch.hpp test/unit.cpp
-)
+enable_testing()
 
-if(MSVC)
-    set(CMAKE_CXX_FLAGS
-        "/EHsc"
-    )
+option(BuildTests "Build the unit tests" ON)
 
-    STRING(REPLACE "/O2" "/Od" CMAKE_CXX_FLAGS_RELEASE ${CMAKE_CXX_FLAGS_RELEASE})
+# define project variables
+set(JSON_TARGET_NAME ${PROJECT_NAME})
+set(JSON_PACKAGE_NAME ${JSON_TARGET_NAME})
+set(JSON_TARGETS_FILENAME "${JSON_PACKAGE_NAME}Targets.cmake")
+set(JSON_CONFIG_FILENAME "${JSON_PACKAGE_NAME}Config.cmake")
+set(JSON_CONFIGVERSION_FILENAME "${JSON_PACKAGE_NAME}ConfigVersion.cmake")
+set(JSON_CONFIG_DESTINATION "cmake")
+set(JSON_INCLUDE_DESTINATION "include/nlohmann")
 
-    add_definitions(-D_SCL_SECURE_NO_WARNINGS)
-else(MSVC)
-    set(CMAKE_CXX_FLAGS
-        "-std=c++11"
-    )
-endif(MSVC)
+# create and configure the library target
+add_library(${JSON_TARGET_NAME} INTERFACE)
+target_include_directories(${JSON_TARGET_NAME} INTERFACE
+  $<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/src>
+  $<INSTALL_INTERFACE:${JSON_INCLUDE_DESTINATION}>)
 
-include_directories(
-    src test
-)
+# create and configure the unit test target
+if (BuildTests)
+    add_subdirectory(test)
+endif()
+
+# generate a config and config version file for the package
+include(CMakePackageConfigHelpers)
+configure_package_config_file("cmake/config.cmake.in"
+    "${CMAKE_CURRENT_BINARY_DIR}/${JSON_CONFIG_FILENAME}"
+    INSTALL_DESTINATION ${JSON_CONFIG_DESTINATION})
+write_basic_package_version_file("${CMAKE_CURRENT_BINARY_DIR}/${JSON_CONFIGVERSION_FILENAME}"
+    VERSION ${PROJECT_VERSION}
+    COMPATIBILITY SameMajorVersion)
+
+# export the library target and store build directory in package registry
+export(TARGETS ${JSON_TARGET_NAME}
+	FILE "${CMAKE_CURRENT_BINARY_DIR}/${JSON_TARGETS_FILENAME}")
+export(PACKAGE ${JSON_PACKAGE_NAME})
+
+# install library target and config files
+install(TARGETS ${JSON_TARGET_NAME}
+	EXPORT ${JSON_PACKAGE_NAME})
+install(FILES "src/json.hpp"
+	DESTINATION ${JSON_INCLUDE_DESTINATION})
+install(EXPORT ${JSON_PACKAGE_NAME}
+	FILE ${JSON_TARGETS_FILENAME}
+	DESTINATION ${JSON_CONFIG_DESTINATION})
+install(FILES "${CMAKE_CURRENT_BINARY_DIR}/${JSON_CONFIG_FILENAME}"
+	"${CMAKE_CURRENT_BINARY_DIR}/${JSON_CONFIGVERSION_FILENAME}"
+	DESTINATION ${JSON_CONFIG_DESTINATION})
diff --git a/ChangeLog.md b/ChangeLog.md
index bb32559..ae9e562 100644
--- a/ChangeLog.md
+++ b/ChangeLog.md
@@ -5,18 +5,52 @@
 
 [Full Changelog](https://github.com/nlohmann/json/compare/v1.1.0...HEAD)
 
+- concatenate objects [\#252](https://github.com/nlohmann/json/issues/252)
+- Unit test fails when doing a CMake out-of-tree build [\#241](https://github.com/nlohmann/json/issues/241)
+- Additional integration options [\#237](https://github.com/nlohmann/json/issues/237)
+- Can't use basic\_json::iterator as a base iterator for std::move\_iterator [\#233](https://github.com/nlohmann/json/issues/233)
 - Provide a FAQ [\#163](https://github.com/nlohmann/json/issues/163)
+- Create PULL\_REQUEST\_TEMPLATE.md [\#213](https://github.com/nlohmann/json/pull/213) ([whackashoe](https://github.com/whackashoe))
+- fixed noexcept; added constexpr [\#208](https://github.com/nlohmann/json/pull/208) ([nlohmann](https://github.com/nlohmann))
+- Add support for afl-fuzz testing [\#207](https://github.com/nlohmann/json/pull/207) ([mykter](https://github.com/mykter))
 - Issue \#178 - Extending support to full uint64\_t/int64\_t range and unsigned type \(updated\) [\#193](https://github.com/nlohmann/json/pull/193) ([twelsby](https://github.com/twelsby))
 
+- json::diff generates incorrect patch when removing multiple array elements. [\#269](https://github.com/nlohmann/json/issues/269)
+- -Wconversion warning [\#259](https://github.com/nlohmann/json/issues/259)
+- Maybe a bug [\#258](https://github.com/nlohmann/json/issues/258)
+- -Wconversion warnings [\#239](https://github.com/nlohmann/json/issues/239)
+- ambiguous overload for 'push\_back' and 'operator+=' [\#235](https://github.com/nlohmann/json/issues/235)
+- Incorrect dumping of parsed numbers with exponents, but without decimal places [\#230](https://github.com/nlohmann/json/issues/230)
+- double values are serialized with commas as decimal points [\#228](https://github.com/nlohmann/json/issues/228)
+- Move semantics with std::initializer\_list [\#225](https://github.com/nlohmann/json/issues/225)
+- abort during getline in yyfill [\#223](https://github.com/nlohmann/json/issues/223)
+- Wrong link in the README.md [\#217](https://github.com/nlohmann/json/issues/217)
 - Small bugs in json.hpp \(get\_number\) and unit.cpp \(non-standard integer type test\) [\#199](https://github.com/nlohmann/json/issues/199)
 - GCC/clang floating point parsing bug in strtod\(\) [\#195](https://github.com/nlohmann/json/issues/195)
 - Bugs in miloyip/nativejson-benchmark: roundtrips [\#187](https://github.com/nlohmann/json/issues/187)
 - Floating point exceptions [\#181](https://github.com/nlohmann/json/issues/181)
+- In basic\_json::basic\_json\(const CompatibleArrayType& val\), the requirement of CompatibleArrayType is not strict enough. [\#174](https://github.com/nlohmann/json/issues/174)
 - Implicit assignment to std::string fails [\#144](https://github.com/nlohmann/json/issues/144)
+- Fix Issue \#265 [\#266](https://github.com/nlohmann/json/pull/266) ([06needhamt](https://github.com/06needhamt))
 - Issue \#195 - update Travis to Trusty due to gcc/clang strtod\(\) bug [\#196](https://github.com/nlohmann/json/pull/196) ([twelsby](https://github.com/twelsby))
 
+- Compiler Errors With JSON.hpp [\#265](https://github.com/nlohmann/json/issues/265)
+- VS2015 compile fail [\#260](https://github.com/nlohmann/json/issues/260)
+- Error when using json in g++ [\#254](https://github.com/nlohmann/json/issues/254)
+- Update long-term goals [\#246](https://github.com/nlohmann/json/issues/246)
+- Error compile Android NDK  error: 'strtof' is not a member of 'std' [\#219](https://github.com/nlohmann/json/issues/219)
+- Conflicting typedef of ssize\_t on Windows 32 bit when using Boost.Python [\#204](https://github.com/nlohmann/json/issues/204)
 - Integer conversion to unsigned [\#178](https://github.com/nlohmann/json/issues/178)
 
+- Define CMake/CTest tests [\#247](https://github.com/nlohmann/json/pull/247) ([robertmrk](https://github.com/robertmrk))
+- Out of tree builds and a few other miscellaneous CMake cleanups. [\#242](https://github.com/nlohmann/json/pull/242) ([ChrisKitching](https://github.com/ChrisKitching))
+- Implement additional integration options [\#238](https://github.com/nlohmann/json/pull/238) ([robertmrk](https://github.com/robertmrk))
+- make serialization locale-independent [\#232](https://github.com/nlohmann/json/pull/232) ([nlohmann](https://github.com/nlohmann))
+- fixes \#223 by updating README.md [\#227](https://github.com/nlohmann/json/pull/227) ([kevin--](https://github.com/kevin--))
+- Use namespace std for int64\_t and uint64\_t [\#226](https://github.com/nlohmann/json/pull/226) ([lv-zheng](https://github.com/lv-zheng))
+- Added missing cerrno header to fix ERANGE compile error on android [\#222](https://github.com/nlohmann/json/pull/222) ([Teemperor](https://github.com/Teemperor))
+- Corrected readme [\#218](https://github.com/nlohmann/json/pull/218) ([Annihil](https://github.com/Annihil))
+- replaced ssize\_t occurrences with auto \(addresses \#204\) [\#205](https://github.com/nlohmann/json/pull/205) ([nlohmann](https://github.com/nlohmann))
 - Fixed issue \#199 - Small bugs in json.hpp \(get\_number\) and unit.cpp \(non-standard integer type test\) [\#200](https://github.com/nlohmann/json/pull/200) ([twelsby](https://github.com/twelsby))
 - Fix broken link [\#197](https://github.com/nlohmann/json/pull/197) ([vog](https://github.com/vog))
 
diff --git a/Makefile b/Makefile
index 91d4fc5..56e46d1 100644
--- a/Makefile
+++ b/Makefile
@@ -10,6 +10,7 @@
 # clean up
 clean:
 	rm -fr json_unit json_benchmarks fuzz fuzz-testing *.dSYM
+	$(MAKE) clean -Cdoc
 
 
 ##########################################################################
@@ -19,8 +20,8 @@
 # additional flags
 FLAGS = -Wall -Wextra -pedantic -Weffc++ -Wcast-align -Wcast-qual -Wctor-dtor-privacy -Wdisabled-optimization -Wformat=2 -Winit-self -Wmissing-declarations -Wmissing-include-dirs -Wold-style-cast -Woverloaded-virtual -Wredundant-decls -Wshadow -Wsign-conversion -Wsign-promo -Wstrict-overflow=5 -Wswitch -Wundef -Wno-unused -Wnon-virtual-dtor -Wreorder -Wdeprecated -Wfloat-equal
 
-# build unit tests
-json_unit: test/unit.cpp src/json.hpp test/catch.hpp
+# build unit tests (TODO: Does this want its own makefile?)
+json_unit: test/src/unit.cpp src/json.hpp test/src/catch.hpp
 	$(CXX) -std=c++11 $(CXXFLAGS) $(FLAGS) $(CPPFLAGS) -I src -I test $< $(LDFLAGS) -o $@
 
 
@@ -30,7 +31,7 @@
 
 # compile example files and check output
 doctest:
-	make check_output -C doc
+	$(MAKE) check_output -C doc
 
 
 ##########################################################################
@@ -43,11 +44,11 @@
 	mkdir -p fuzz-testing fuzz-testing/testcases fuzz-testing/out
 	$(MAKE) fuzz CXX=afl-clang++
 	mv fuzz fuzz-testing
-	find test/json_tests -size -5k -name *json | xargs -I{} cp "{}" fuzz-testing/testcases
+	find test/data/json_tests -size -5k -name *json | xargs -I{} cp "{}" fuzz-testing/testcases
 	@echo "Execute: afl-fuzz -i fuzz-testing/testcases -o fuzz-testing/out fuzz-testing/fuzz"
 
 # the fuzzer binary
-fuzz: test/fuzz.cpp src/json.hpp
+fuzz: test/src/fuzz.cpp src/json.hpp
 	$(CXX) -std=c++11 $(CXXFLAGS) $(FLAGS) $(CPPFLAGS) -I src $< $(LDFLAGS) -o $@
 
 
@@ -66,7 +67,7 @@
 
 # create scanner with re2c
 re2c: src/json.hpp.re2c
-	$(RE2C) -b -s -i --no-generation-date $< | $(SED) '1d' > src/json.hpp
+	$(RE2C) -W --bit-vectors --nested-ifs --no-debug-info $< | $(SED) '1d' > src/json.hpp
 
 # pretty printer
 pretty:
@@ -74,8 +75,8 @@
 	   --indent-switches --indent-preproc-block --indent-preproc-define \
 	   --indent-col1-comments --pad-oper --pad-header --align-pointer=type \
 	   --align-reference=type --add-brackets --convert-tabs --close-templates \
-	   --lineend=linux --preserve-date --suffix=none \
-	   src/json.hpp src/json.hpp.re2c test/unit.cpp test/fuzz.cpp benchmarks/benchmarks.cpp doc/examples/*.cpp
+	   --lineend=linux --preserve-date --suffix=none --formatted \
+	   src/json.hpp src/json.hpp.re2c test/src/unit.cpp test/src/fuzz.cpp benchmarks/benchmarks.cpp doc/examples/*.cpp
 
 
 ##########################################################################
diff --git a/README.md b/README.md
index 2fadeff..21ff3c5 100644
--- a/README.md
+++ b/README.md
@@ -3,8 +3,9 @@
 [![Build Status](https://travis-ci.org/nlohmann/json.svg?branch=master)](https://travis-ci.org/nlohmann/json)
 [![Build Status](https://ci.appveyor.com/api/projects/status/1acb366xfyg3qybk?svg=true)](https://ci.appveyor.com/project/nlohmann/json)
 [![Coverage Status](https://img.shields.io/coveralls/nlohmann/json.svg)](https://coveralls.io/r/nlohmann/json)
+[![Coverity Scan Build Status](https://scan.coverity.com/projects/5550/badge.svg)](https://scan.coverity.com/projects/nlohmann-json)
 [![Try online](https://img.shields.io/badge/try-online-blue.svg)](http://melpon.org/wandbox/permlink/wuiuqYiYqRTdI3rG)
-[![Documentation Status](https://img.shields.io/badge/docs-doxygen-blue.svg)](http://nlohmann.github.io/json)
+[![Documentation](https://img.shields.io/badge/docs-doxygen-blue.svg)](http://nlohmann.github.io/json)
 [![GitHub license](https://img.shields.io/badge/license-MIT-blue.svg)](https://raw.githubusercontent.com/nlohmann/json/master/LICENSE.MIT)
 [![Github Releases](https://img.shields.io/github/release/nlohmann/json.svg)](https://github.com/nlohmann/json/releases)
 [![Github Issues](https://img.shields.io/github/issues/nlohmann/json.svg)](http://github.com/nlohmann/json/issues)
@@ -15,9 +16,9 @@
 
 - **Intuitive syntax**. In languages such as Python, JSON feels like a first class data type. We used all the operator magic of modern C++ to achieve the same feeling in your code. Check out the [examples below](#examples) and you know, what I mean.
 
-- **Trivial integration**. Our whole code consists of a single header file `json.hpp`. That's it. No library, no subproject, no dependencies, no complex build system. The class is written in vanilla C++11. All in all, everything should require no adjustment of your compiler flags or project settings.
+- **Trivial integration**. Our whole code consists of a single header file [`json.hpp`](https://github.com/nlohmann/json/blob/develop/src/json.hpp). That's it. No library, no subproject, no dependencies, no complex build system. The class is written in vanilla C++11. All in all, everything should require no adjustment of your compiler flags or project settings.
 
-- **Serious testing**. Our class is heavily [unit-tested](https://github.com/nlohmann/json/blob/master/test/unit.cpp) and covers [100%](https://coveralls.io/r/nlohmann/json) of the code, including all exceptional behavior. Furthermore, we checked with [Valgrind](http://valgrind.org) that there are no memory leaks.
+- **Serious testing**. Our class is heavily [unit-tested](https://github.com/nlohmann/json/blob/master/test/src/unit.cpp) and covers [100%](https://coveralls.io/r/nlohmann/json) of the code, including all exceptional behavior. Furthermore, we checked with [Valgrind](http://valgrind.org) that there are no memory leaks.
 
 Other aspects were not so important to us:
 
@@ -27,6 +28,7 @@
 
 See the [contribution guidelines](https://github.com/nlohmann/json/blob/master/.github/CONTRIBUTING.md#please-dont) for more information.
 
+
 ## Integration
 
 The single required source, file `json.hpp` is in the `src` directory or [released here](https://github.com/nlohmann/json/releases). All you need to do is add
@@ -40,28 +42,8 @@
 
 to the files you want to use JSON objects. That's it. Do not forget to set the necessary switches to enable C++11 (e.g., `-std=c++11` for GCC and Clang).
 
-## Supported compilers
+:beer: If you are using OS X and [Homebrew](http://brew.sh), just type `brew tap nlohmann/json` and `brew install nlohmann_json` and you're set. If you want the bleeding edge rather than the latest release, use `brew install nlohmann_json --HEAD`.
 
-Though it's 2016 already, the support for C++11 is still a bit sparse. Currently, the following compilers are known to work:
-
-- GCC 4.9 - 6.0 (and possibly later)
-- Clang 3.4 - 3.9 (and possibly later)
-- Microsoft Visual C++ 14.0 RC (and possibly later)
-
-I would be happy to learn about other compilers/versions.
-
-Please note:
-
-- GCC 4.8 does not work because of two bugs ([55817](https://gcc.gnu.org/bugzilla/show_bug.cgi?id=55817) and [57824](https://gcc.gnu.org/bugzilla/show_bug.cgi?id=57824)) in the C++11 support. Note there is a [pull request](https://github.com/nlohmann/json/pull/212) to fix some of the issues.
-- Android defaults to using very old compilers and C++ libraries. To fix this, add the following to your `Application.mk`. This will switch to the LLVM C++ library, the Clang compiler, and enable C++11 and other features disabled by default.
- 
-```
-APP_STL := c++_shared
-NDK_TOOLCHAIN_VERSION := clang3.6
-APP_CPPFLAGS += -frtti -fexceptions
-```
- 
-- For GCC running on MinGW or Android SDK, the error `'to_string' is not a member of 'std'` (or similarly, for `strtod`) may occur. Note this is not an issue with the code,  but rather with the compiler itself. On Android, see above to build with a newer environment.  For MingW, please refer to [this site](http://tehsausage.com/mingw-to-string) and [this discussion](https://github.com/nlohmann/json/issues/136) for information on how to fix this bug.
 
 ## Examples
 
@@ -144,6 +126,7 @@
 json array_not_object = { json::array({"currency", "USD"}), json::array({"value", 42.99}) };
 ```
 
+
 ### Serialization / Deserialization
 
 You can create an object (deserialization) by appending `_json` to a string literal:
@@ -152,7 +135,7 @@
 // create object from string literal
 json j = "{ \"happy\": true, \"pi\": 3.141 }"_json;
 
-// or even nicer (thanks http://isocpp.org/blog/2015/01/json-for-modern-cpp)
+// or even nicer with a raw string literal
 auto j2 = R"(
   {
     "happy": true,
@@ -197,6 +180,7 @@
 
 Please note that setting the exception bit for `failbit` is inappropriate for this use case. It will result in program termination due to the `noexcept` specifier in use.
 
+
 ### STL-like access
 
 We designed the JSON class to behave just like an STL container. In fact, it satisfies the [**ReversibleContainer**](http://en.cppreference.com/w/cpp/concept/ReversibleContainer) requirement.
@@ -264,6 +248,7 @@
 o.erase("foo");
 ```
 
+
 ### Conversion from STL containers
 
 Any sequence container (`std::array`, `std::vector`, `std::deque`, `std::forward_list`, `std::list`) whose values can be used to construct JSON types (e.g., integers, floating point numbers, Booleans, string types, or again STL containers described in this section) can be used to create a JSON array. The same holds for similar associative containers (`std::set`, `std::multiset`, `std::unordered_set`, `std::unordered_multiset`), but in these cases the order of the elements of the array depends how the elements are ordered in the respective STL container.
@@ -326,12 +311,51 @@
 // maybe {"one": true, "two": true, "three": true}
 ```
 
+### JSON Pointer and JSON Patch
+
+The library supports **JSON Pointer** ([RFC 6901](https://tools.ietf.org/html/rfc6901)) as alternative means to address structured values. On top of this, **JSON Patch** ([RFC 6902](https://tools.ietf.org/html/rfc6902)) allows to describe differences between two JSON values - effectively allowing patch and diff operations known from Unix.
+
+```cpp
+// a JSON value
+json j_original = R"({
+  "baz": ["one", "two", "three"],
+  "foo": "bar"
+})"_json;
+
+// access members with a JSON pointer (RFC 6901)
+j_original["/baz/2"_json_pointer];
+// "two"
+
+// a JSON patch (RFC 6902)
+json j_patch = R"([
+  { "op": "replace", "path": "/baz", "value": "boo" },
+  { "op": "add", "path": "/hello", "value": ["world"] },
+  { "op": "remove", "path": "/foo"}
+])"_json;
+
+// apply the patch
+json j_result = j_original.patch(j_patch);
+// {
+//    "baz": "boo",
+//    "hello": ["world"]
+// }
+
+// calculate a JSON patch from two JSON values
+json::diff(j_result, j_original);
+// [
+//   { "op":" replace", "path": "/baz", "value": ["one", "two", "three"] },
+//   { "op":"remove","path":"/hello" },
+//   { "op":"add","path":"/foo","value":"bar" }
+// ]
+```
+
+
 ### Implicit conversions
 
 The type of the JSON object is determined automatically by the expression to store. Likewise, the stored value is implicitly converted.
 
 ```cpp
-/// strings
+// strings
 std::string s1 = "Hello, world!";
 json js = s1;
 std::string s2 = js;
@@ -359,6 +383,51 @@
 // etc.
 ```
 
+
+## Supported compilers
+
+Though it's 2016 already, the support for C++11 is still a bit sparse. Currently, the following compilers are known to work:
+
+- GCC 4.9 - 6.0 (and possibly later)
+- Clang 3.4 - 3.9 (and possibly later)
+- Microsoft Visual C++ 2015 / 14.0 (and possibly later)
+
+I would be happy to learn about other compilers/versions.
+
+Please note:
+
+- GCC 4.8 does not work because of two bugs ([55817](https://gcc.gnu.org/bugzilla/show_bug.cgi?id=55817) and [57824](https://gcc.gnu.org/bugzilla/show_bug.cgi?id=57824)) in the C++11 support. Note there is a [pull request](https://github.com/nlohmann/json/pull/212) to fix some of the issues.
+- Android defaults to using very old compilers and C++ libraries. To fix this, add the following to your `Application.mk`. This will switch to the LLVM C++ library, the Clang compiler, and enable C++11 and other features disabled by default.
+ 
+    ```
+    APP_STL := c++_shared
+    NDK_TOOLCHAIN_VERSION := clang3.6
+    APP_CPPFLAGS += -frtti -fexceptions
+    ```
+ 
+    The code compiles successfully with [Android NDK](https://developer.android.com/ndk/index.html?hl=ml), Revision 9 - 11 (and possibly later) and [CrystaX's Android NDK](https://www.crystax.net/en/android/ndk) version 10.
+
+- For GCC running on MinGW or Android SDK, the error `'to_string' is not a member of 'std'` (or similarly, for `strtod`) may occur. Note this is not an issue with the code,  but rather with the compiler itself. On Android, see above to build with a newer environment.  For MinGW, please refer to [this site](http://tehsausage.com/mingw-to-string) and [this discussion](https://github.com/nlohmann/json/issues/136) for information on how to fix this bug. For Android NDK using `APP_STL := gnustl_static`, please refer to [this discussion](https://github.com/nlohmann/json/issues/219).
+
+The following compilers are currently used in continuous integration at [Travis](https://travis-ci.org/nlohmann/json) and [AppVeyor](https://ci.appveyor.com/project/nlohmann/json):
+
+| Compiler        | Operating System             | Version String |
+|-----------------|------------------------------|----------------|
+| GCC 4.9.3       | Ubuntu 14.04.4 LTS           | g++-4.9 (Ubuntu 4.9.3-8ubuntu2~14.04) 4.9.3 |
+| GCC 5.3.0       | Ubuntu 14.04.4 LTS           | g++-5 (Ubuntu 5.3.0-3ubuntu1~14.04) 5.3.0 20151204 |
+| GCC 6.1.1       | Ubuntu 14.04.4 LTS           | g++-6 (Ubuntu 6.1.1-3ubuntu11~14.04.1) 6.1.1 20160511 |
+| Clang 3.8.0     | Ubuntu 14.04.4 LTS           | clang version 3.8.0 (tags/RELEASE_380/final) |
+| Clang Xcode 6.1 | Darwin Kernel Version 13.4.0 (OSX 10.9.5) | Apple LLVM version 6.0 (clang-600.0.54) (based on LLVM 3.5svn) |
+| Clang Xcode 6.2 | Darwin Kernel Version 13.4.0 (OSX 10.9.5) | Apple LLVM version 6.0 (clang-600.0.57) (based on LLVM 3.5svn) |
+| Clang Xcode 6.3 | Darwin Kernel Version 14.3.0 (OSX 10.10.3) | Apple LLVM version 6.1.0 (clang-602.0.49) (based on LLVM 3.6.0svn) |
+| Clang Xcode 6.4 | Darwin Kernel Version 14.3.0 (OSX 10.10.3) | Apple LLVM version 6.1.0 (clang-602.0.53) (based on LLVM 3.6.0svn) |
+| Clang Xcode 7.1 | Darwin Kernel Version 14.5.0 (OSX 10.10.5) | Apple LLVM version 7.0.0 (clang-700.1.76) |
+| Clang Xcode 7.2 | Darwin Kernel Version 15.0.0 (OSX 10.10.5) | Apple LLVM version 7.0.2 (clang-700.1.81) |
+| Clang Xcode 7.3 | Darwin Kernel Version 15.0.0 (OSX 10.10.5) | Apple LLVM version 7.3.0 (clang-703.0.29) |
+| Clang Xcode 8.0 | Darwin Kernel Version 15.5.0 (OSX 10.11.5) | Apple LLVM version 8.0.0 (clang-800.0.24.1) |
+| Visual Studio 14 2015 | Windows Server 2012 R2 (x64) | Microsoft (R) Build Engine version 14.0.25123.0 | 
+
+
 ## License
 
 <img align="right" src="http://opensource.org/trademarks/opensource/OSI-Approved-License-100x137.png">
@@ -367,11 +436,12 @@
 
 Copyright &copy; 2013-2016 [Niels Lohmann](http://nlohmann.me)
 
-Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
+Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the “Software”), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
 
 The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
 
-THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+THE 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.
+
 
 ## Thanks
 
@@ -410,14 +480,19 @@
 - [Lv Zheng](https://github.com/lv-zheng) fixed a namespace issue with `int64_t` and `uint64_t`.
 - [abc100m](https://github.com/abc100m) analyzed the issues with GCC 4.8 and proposed a [partial solution](https://github.com/nlohmann/json/pull/212).
 - [zewt](https://github.com/zewt) added useful notes to the README file about Android.
+- [Róbert Márki](https://github.com/robertmrk) added a fix to use move iterators and improved the integration via CMake.
+- [Chris Kitching](https://github.com/ChrisKitching) cleaned up the CMake files.
+- [Tom Needham](https://github.com/06needhamt) fixed a subtle bug with MSVC 2015 which was also proposed by [Michael K.](https://github.com/Epidal).
 
 Thanks a lot for helping out!
 
+
 ## Notes
 
 - The code contains numerous debug **assertions** which can be switched off by defining the preprocessor macro `NDEBUG`, see the [documentation of `assert`](http://en.cppreference.com/w/cpp/error/assert).
 - As the exact type of a number is not defined in the [JSON specification](http://rfc7159.net/rfc7159), this library tries to choose the best fitting C++ number type automatically. As a result, the type `double` may be used to store numbers which may yield [**floating-point exceptions**](https://github.com/nlohmann/json/issues/181) in certain rare situations if floating-point exceptions have been unmasked in the calling code. These exceptions are not caused by the library and need to be fixed in the calling code, such as by re-masking the exceptions prior to calling library functions.
 
+
 ## Execute unit tests
 
 To compile and run the tests, you need to execute
@@ -427,7 +502,7 @@
 $ ./json_unit "*"
 
 ===============================================================================
-All tests passed (3344278 assertions in 29 test cases)
+All tests passed (5568715 assertions in 32 test cases)
 ```
 
 For more information, have a look at the file [.travis.yml](https://github.com/nlohmann/json/blob/master/.travis.yml).
diff --git a/appveyor.yml b/appveyor.yml
index aaf9c5f..a527ef7 100644
--- a/appveyor.yml
+++ b/appveyor.yml
@@ -7,5 +7,4 @@
 - cmake . -G "Visual Studio 14 2015"

 - cmake --build . --config Release

 test_script:

-- Release\json_unit.exe

-- Release\json_unit.exe "*"

+- ctest -C Release -V

diff --git a/cmake/config.cmake.in b/cmake/config.cmake.in
new file mode 100644
index 0000000..3ff97ad
--- /dev/null
+++ b/cmake/config.cmake.in
@@ -0,0 +1,6 @@
+@PACKAGE_INIT@
+
+cmake_policy(PUSH)
+cmake_policy(SET CMP0024 OLD)
+include(${CMAKE_CURRENT_LIST_DIR}/@JSON_TARGETS_FILENAME@)
+cmake_policy(POP)
\ No newline at end of file
diff --git a/doc/Doxyfile b/doc/Doxyfile
index 11faf6d..e74a8a8 100644
--- a/doc/Doxyfile
+++ b/doc/Doxyfile
@@ -236,7 +236,7 @@
 #---------------------------------------------------------------------------
 # Configuration options related to the XML output
 #---------------------------------------------------------------------------
-GENERATE_XML           = NO
+GENERATE_XML           = YES
 XML_OUTPUT             = xml
 XML_PROGRAMLISTING     = YES
 #---------------------------------------------------------------------------
diff --git a/doc/Makefile b/doc/Makefile
index dc10f1d..5e5459a 100644
--- a/doc/Makefile
+++ b/doc/Makefile
@@ -2,9 +2,6 @@
 
 all: doxygen
 
-clean:
-	rm -fr me.nlohmann.json.docset html
-
 
 ##########################################################################
 # example files
@@ -44,6 +41,10 @@
 check_output: $(EXAMPLES:.cpp=.test)
 
 
+clean:
+	rm -fr me.nlohmann.json.docset html $(EXAMPLES:.cpp=)
+
+
 ##########################################################################
 # Doxygen HTML documentation
 ##########################################################################
diff --git a/doc/examples/README.link b/doc/examples/README.link
index a2526dc..ed559e1 100644
--- a/doc/examples/README.link
+++ b/doc/examples/README.link
@@ -1 +1 @@
-<a target="_blank" href="http://melpon.org/wandbox/permlink/fYyscqrsQFtgUePA"><b>online</b></a>
\ No newline at end of file
+<a target="_blank" href="http://melpon.org/wandbox/permlink/k5KRwVJ42VggxxzX"><b>online</b></a>
\ No newline at end of file
diff --git a/doc/examples/array.link b/doc/examples/array.link
index 626dcd2..cafca79 100644
--- a/doc/examples/array.link
+++ b/doc/examples/array.link
@@ -1 +1 @@
-<a target="_blank" href="http://melpon.org/wandbox/permlink/TTykAqoDgbLzKxt3"><b>online</b></a>
\ No newline at end of file
+<a target="_blank" href="http://melpon.org/wandbox/permlink/1fsm4gI55p83DOwU"><b>online</b></a>
\ No newline at end of file
diff --git a/doc/examples/at__object_t_key_type.link b/doc/examples/at__object_t_key_type.link
index f7e44b2..e01197e 100644
--- a/doc/examples/at__object_t_key_type.link
+++ b/doc/examples/at__object_t_key_type.link
@@ -1 +1 @@
-<a target="_blank" href="http://melpon.org/wandbox/permlink/wjOUJUwo2SO7q5u8"><b>online</b></a>
\ No newline at end of file
+<a target="_blank" href="http://melpon.org/wandbox/permlink/rsnwWxpDjmtRRSzb"><b>online</b></a>
\ No newline at end of file
diff --git a/doc/examples/at__object_t_key_type_const.link b/doc/examples/at__object_t_key_type_const.link
index 8ba2116..2588529 100644
--- a/doc/examples/at__object_t_key_type_const.link
+++ b/doc/examples/at__object_t_key_type_const.link
@@ -1 +1 @@
-<a target="_blank" href="http://melpon.org/wandbox/permlink/7gGtalpuoF4xE7VN"><b>online</b></a>
\ No newline at end of file
+<a target="_blank" href="http://melpon.org/wandbox/permlink/aeoZrnhnb3HKClCY"><b>online</b></a>
\ No newline at end of file
diff --git a/doc/examples/at__size_type.link b/doc/examples/at__size_type.link
index 76a409c..00e42d3 100644
--- a/doc/examples/at__size_type.link
+++ b/doc/examples/at__size_type.link
@@ -1 +1 @@
-<a target="_blank" href="http://melpon.org/wandbox/permlink/Sa6XYQS7TJ5sGG8a"><b>online</b></a>
\ No newline at end of file
+<a target="_blank" href="http://melpon.org/wandbox/permlink/R7z2SB2rMdFQ9XtR"><b>online</b></a>
\ No newline at end of file
diff --git a/doc/examples/at__size_type_const.link b/doc/examples/at__size_type_const.link
index 5a1cdde..0fefb62 100644
--- a/doc/examples/at__size_type_const.link
+++ b/doc/examples/at__size_type_const.link
@@ -1 +1 @@
-<a target="_blank" href="http://melpon.org/wandbox/permlink/PGQOjyIV7s5YnsQM"><b>online</b></a>
\ No newline at end of file
+<a target="_blank" href="http://melpon.org/wandbox/permlink/L1bMeiN6nYm7JrvA"><b>online</b></a>
\ No newline at end of file
diff --git a/doc/examples/at_json_pointer.cpp b/doc/examples/at_json_pointer.cpp
new file mode 100644
index 0000000..0665e60
--- /dev/null
+++ b/doc/examples/at_json_pointer.cpp
@@ -0,0 +1,35 @@
+#include <json.hpp>
+
+using json = nlohmann::json;
+
+int main()
+{
+    // create a JSON value
+    json j =
+    {
+        {"number", 1}, {"string", "foo"}, {"array", {1, 2}}
+    };
+
+    // read-only access
+
+    // output element with JSON pointer "/number"
+    std::cout << j.at("/number"_json_pointer) << '\n';
+    // output element with JSON pointer "/string"
+    std::cout << j.at("/string"_json_pointer) << '\n';
+    // output element with JSON pointer "/array"
+    std::cout << j.at("/array"_json_pointer) << '\n';
+    // output element with JSON pointer "/array/1"
+    std::cout << j.at("/array/1"_json_pointer) << '\n';
+
+    // writing access
+
+    // change the string
+    j.at("/string"_json_pointer) = "bar";
+    // output the changed string
+    std::cout << j["string"] << '\n';
+
+    // change an array element
+    j.at("/array/1"_json_pointer) = 21;
+    // output the changed array
+    std::cout << j["array"] << '\n';
+}
diff --git a/doc/examples/at_json_pointer.link b/doc/examples/at_json_pointer.link
new file mode 100644
index 0000000..7a7efa2
--- /dev/null
+++ b/doc/examples/at_json_pointer.link
@@ -0,0 +1 @@
+<a target="_blank" href="http://melpon.org/wandbox/permlink/NDSjglHZIjIZ0Uxg"><b>online</b></a>
\ No newline at end of file
diff --git a/doc/examples/at_json_pointer.output b/doc/examples/at_json_pointer.output
new file mode 100644
index 0000000..11913c7
--- /dev/null
+++ b/doc/examples/at_json_pointer.output
@@ -0,0 +1,6 @@
+1
+"foo"
+[1,2]
+2
+"bar"
+[1,21]
diff --git a/doc/examples/at_json_pointer_const.cpp b/doc/examples/at_json_pointer_const.cpp
new file mode 100644
index 0000000..e3cfc51
--- /dev/null
+++ b/doc/examples/at_json_pointer_const.cpp
@@ -0,0 +1,23 @@
+#include <json.hpp>
+
+using json = nlohmann::json;
+
+int main()
+{
+    // create a JSON value
+    json j =
+    {
+        {"number", 1}, {"string", "foo"}, {"array", {1, 2}}
+    };
+
+    // read-only access
+
+    // output element with JSON pointer "/number"
+    std::cout << j.at("/number"_json_pointer) << '\n';
+    // output element with JSON pointer "/string"
+    std::cout << j.at("/string"_json_pointer) << '\n';
+    // output element with JSON pointer "/array"
+    std::cout << j.at("/array"_json_pointer) << '\n';
+    // output element with JSON pointer "/array/1"
+    std::cout << j.at("/array/1"_json_pointer) << '\n';
+}
diff --git a/doc/examples/at_json_pointer_const.link b/doc/examples/at_json_pointer_const.link
new file mode 100644
index 0000000..9057e0b
--- /dev/null
+++ b/doc/examples/at_json_pointer_const.link
@@ -0,0 +1 @@
+<a target="_blank" href="http://melpon.org/wandbox/permlink/OuLYiMJ3pgyOHupb"><b>online</b></a>
\ No newline at end of file
diff --git a/doc/examples/at_json_pointer_const.output b/doc/examples/at_json_pointer_const.output
new file mode 100644
index 0000000..7b9306b
--- /dev/null
+++ b/doc/examples/at_json_pointer_const.output
@@ -0,0 +1,4 @@
+1
+"foo"
+[1,2]
+2
diff --git a/doc/examples/back.link b/doc/examples/back.link
index 49d008f..0b00978 100644
--- a/doc/examples/back.link
+++ b/doc/examples/back.link
@@ -1 +1 @@
-<a target="_blank" href="http://melpon.org/wandbox/permlink/V7lUsd6LyndZDGoM"><b>online</b></a>
\ No newline at end of file
+<a target="_blank" href="http://melpon.org/wandbox/permlink/nPVnBcHf8nrNpGOJ"><b>online</b></a>
\ No newline at end of file
diff --git a/doc/examples/basic_json.link b/doc/examples/basic_json.link
index 1625c48..e5c17c9 100644
--- a/doc/examples/basic_json.link
+++ b/doc/examples/basic_json.link
@@ -1 +1 @@
-<a target="_blank" href="http://melpon.org/wandbox/permlink/mSAAr3GATT6ciVjV"><b>online</b></a>
\ No newline at end of file
+<a target="_blank" href="http://melpon.org/wandbox/permlink/dRptmFmhvpsYB49t"><b>online</b></a>
\ No newline at end of file
diff --git a/doc/examples/basic_json__CompatibleArrayType.link b/doc/examples/basic_json__CompatibleArrayType.link
index 313ae3f..85f6486 100644
--- a/doc/examples/basic_json__CompatibleArrayType.link
+++ b/doc/examples/basic_json__CompatibleArrayType.link
@@ -1 +1 @@
-<a target="_blank" href="http://melpon.org/wandbox/permlink/InYach9gNBDvA8QR"><b>online</b></a>
\ No newline at end of file
+<a target="_blank" href="http://melpon.org/wandbox/permlink/MghSilgvyh4ZJShY"><b>online</b></a>
\ No newline at end of file
diff --git a/doc/examples/basic_json__CompatibleIntegerNumberType.link b/doc/examples/basic_json__CompatibleIntegerNumberType.link
index f994bab..7a37e9e 100644
--- a/doc/examples/basic_json__CompatibleIntegerNumberType.link
+++ b/doc/examples/basic_json__CompatibleIntegerNumberType.link
@@ -1 +1 @@
-<a target="_blank" href="http://melpon.org/wandbox/permlink/VDu3HkQPf0Rv5dzH"><b>online</b></a>
\ No newline at end of file
+<a target="_blank" href="http://melpon.org/wandbox/permlink/PcMzhcu2RpD7KSwr"><b>online</b></a>
\ No newline at end of file
diff --git a/doc/examples/basic_json__CompatibleNumberFloatType.link b/doc/examples/basic_json__CompatibleNumberFloatType.link
index 9e08b6a..9fbc731 100644
--- a/doc/examples/basic_json__CompatibleNumberFloatType.link
+++ b/doc/examples/basic_json__CompatibleNumberFloatType.link
@@ -1 +1 @@
-<a target="_blank" href="http://melpon.org/wandbox/permlink/BjDrTHvJMwFC3iLr"><b>online</b></a>
\ No newline at end of file
+<a target="_blank" href="http://melpon.org/wandbox/permlink/2TCYWSrOxnR05AZI"><b>online</b></a>
\ No newline at end of file
diff --git a/doc/examples/basic_json__CompatibleObjectType.link b/doc/examples/basic_json__CompatibleObjectType.link
index 6d65b17..7512fb3 100644
--- a/doc/examples/basic_json__CompatibleObjectType.link
+++ b/doc/examples/basic_json__CompatibleObjectType.link
@@ -1 +1 @@
-<a target="_blank" href="http://melpon.org/wandbox/permlink/ZJz4SFD4Iuy8OS4T"><b>online</b></a>
\ No newline at end of file
+<a target="_blank" href="http://melpon.org/wandbox/permlink/JzLCMcSXNsh4uVWa"><b>online</b></a>
\ No newline at end of file
diff --git a/doc/examples/basic_json__CompatibleStringType.link b/doc/examples/basic_json__CompatibleStringType.link
index 2131a16..351d6c0 100644
--- a/doc/examples/basic_json__CompatibleStringType.link
+++ b/doc/examples/basic_json__CompatibleStringType.link
@@ -1 +1 @@
-<a target="_blank" href="http://melpon.org/wandbox/permlink/Fe1WNG7eJRaWYMM4"><b>online</b></a>
\ No newline at end of file
+<a target="_blank" href="http://melpon.org/wandbox/permlink/b9hbCY8zfOiTOuoK"><b>online</b></a>
\ No newline at end of file
diff --git a/doc/examples/basic_json__InputIt_InputIt.link b/doc/examples/basic_json__InputIt_InputIt.link
index 3ce3686..ae46528 100644
--- a/doc/examples/basic_json__InputIt_InputIt.link
+++ b/doc/examples/basic_json__InputIt_InputIt.link
@@ -1 +1 @@
-<a target="_blank" href="http://melpon.org/wandbox/permlink/Ulv0RYN6QoJ6ECVR"><b>online</b></a>
\ No newline at end of file
+<a target="_blank" href="http://melpon.org/wandbox/permlink/nKF1QcieoCHm6Lez"><b>online</b></a>
\ No newline at end of file
diff --git a/doc/examples/basic_json__array_t.link b/doc/examples/basic_json__array_t.link
index 5332283..70c9cb8 100644
--- a/doc/examples/basic_json__array_t.link
+++ b/doc/examples/basic_json__array_t.link
@@ -1 +1 @@
-<a target="_blank" href="http://melpon.org/wandbox/permlink/T5FlYwVpQ1oXBuBH"><b>online</b></a>
\ No newline at end of file
+<a target="_blank" href="http://melpon.org/wandbox/permlink/dTbSNAvl6TqrMEAn"><b>online</b></a>
\ No newline at end of file
diff --git a/doc/examples/basic_json__basic_json.link b/doc/examples/basic_json__basic_json.link
index c3109d1..78fcbbe 100644
--- a/doc/examples/basic_json__basic_json.link
+++ b/doc/examples/basic_json__basic_json.link
@@ -1 +1 @@
-<a target="_blank" href="http://melpon.org/wandbox/permlink/vPBv5smAPKUko4Qo"><b>online</b></a>
\ No newline at end of file
+<a target="_blank" href="http://melpon.org/wandbox/permlink/6Qfn12BDzb3vqO5p"><b>online</b></a>
\ No newline at end of file
diff --git a/doc/examples/basic_json__boolean_t.link b/doc/examples/basic_json__boolean_t.link
index 9fe211e..c64e1fc 100644
--- a/doc/examples/basic_json__boolean_t.link
+++ b/doc/examples/basic_json__boolean_t.link
@@ -1 +1 @@
-<a target="_blank" href="http://melpon.org/wandbox/permlink/saCwetmQZaB8tQ24"><b>online</b></a>
\ No newline at end of file
+<a target="_blank" href="http://melpon.org/wandbox/permlink/VmVl9pyrQp8LyOnU"><b>online</b></a>
\ No newline at end of file
diff --git a/doc/examples/basic_json__const_int.link b/doc/examples/basic_json__const_int.link
index 73d2862..68a9e23 100644
--- a/doc/examples/basic_json__const_int.link
+++ b/doc/examples/basic_json__const_int.link
@@ -1 +1 @@
-<a target="_blank" href="http://melpon.org/wandbox/permlink/BzGFT02b2KTR6z2B"><b>online</b></a>
\ No newline at end of file
+<a target="_blank" href="http://melpon.org/wandbox/permlink/3xQ1qy7BT9OrSSCo"><b>online</b></a>
\ No newline at end of file
diff --git a/doc/examples/basic_json__copyassignment.link b/doc/examples/basic_json__copyassignment.link
index 96679cf..efcac30 100644
--- a/doc/examples/basic_json__copyassignment.link
+++ b/doc/examples/basic_json__copyassignment.link
@@ -1 +1 @@
-<a target="_blank" href="http://melpon.org/wandbox/permlink/Ib1A0jrFXwYhf0eA"><b>online</b></a>
\ No newline at end of file
+<a target="_blank" href="http://melpon.org/wandbox/permlink/iWYvmlA4qQmtvACX"><b>online</b></a>
\ No newline at end of file
diff --git a/doc/examples/basic_json__istream.link b/doc/examples/basic_json__istream.link
index e547543..20d1033 100644
--- a/doc/examples/basic_json__istream.link
+++ b/doc/examples/basic_json__istream.link
@@ -1 +1 @@
-<a target="_blank" href="http://melpon.org/wandbox/permlink/lUltfC5TlmxeUq5X"><b>online</b></a>
\ No newline at end of file
+<a target="_blank" href="http://melpon.org/wandbox/permlink/VzSqLszbnoWE92dD"><b>online</b></a>
\ No newline at end of file
diff --git a/doc/examples/basic_json__list_init_t.link b/doc/examples/basic_json__list_init_t.link
index 5eca205..bcdf668 100644
--- a/doc/examples/basic_json__list_init_t.link
+++ b/doc/examples/basic_json__list_init_t.link
@@ -1 +1 @@
-<a target="_blank" href="http://melpon.org/wandbox/permlink/r7SIosyJCePZZvqh"><b>online</b></a>
\ No newline at end of file
+<a target="_blank" href="http://melpon.org/wandbox/permlink/iZzzxEpB7tbhz0cx"><b>online</b></a>
\ No newline at end of file
diff --git a/doc/examples/basic_json__moveconstructor.link b/doc/examples/basic_json__moveconstructor.link
index 88002bb..9580d02 100644
--- a/doc/examples/basic_json__moveconstructor.link
+++ b/doc/examples/basic_json__moveconstructor.link
@@ -1 +1 @@
-<a target="_blank" href="http://melpon.org/wandbox/permlink/y7ngrkT74SN0fFWG"><b>online</b></a>
\ No newline at end of file
+<a target="_blank" href="http://melpon.org/wandbox/permlink/CaDlatv1uXhQiu7o"><b>online</b></a>
\ No newline at end of file
diff --git a/doc/examples/basic_json__nullptr_t.link b/doc/examples/basic_json__nullptr_t.link
index bb26af4..7e91775 100644
--- a/doc/examples/basic_json__nullptr_t.link
+++ b/doc/examples/basic_json__nullptr_t.link
@@ -1 +1 @@
-<a target="_blank" href="http://melpon.org/wandbox/permlink/Y7B27Mtrcp64PI9l"><b>online</b></a>
\ No newline at end of file
+<a target="_blank" href="http://melpon.org/wandbox/permlink/PMMpoM0ujdJDsuta"><b>online</b></a>
\ No newline at end of file
diff --git a/doc/examples/basic_json__number_float_t.link b/doc/examples/basic_json__number_float_t.link
index a7c6b60..47aa255 100644
--- a/doc/examples/basic_json__number_float_t.link
+++ b/doc/examples/basic_json__number_float_t.link
@@ -1 +1 @@
-<a target="_blank" href="http://melpon.org/wandbox/permlink/rsIsQEvaeJEaJx9c"><b>online</b></a>
\ No newline at end of file
+<a target="_blank" href="http://melpon.org/wandbox/permlink/OTgOxjIAKFvxpFdm"><b>online</b></a>
\ No newline at end of file
diff --git a/doc/examples/basic_json__number_integer_t.link b/doc/examples/basic_json__number_integer_t.link
index 764aa64..5d4499b 100644
--- a/doc/examples/basic_json__number_integer_t.link
+++ b/doc/examples/basic_json__number_integer_t.link
@@ -1 +1 @@
-<a target="_blank" href="http://melpon.org/wandbox/permlink/3aV9OONj8OfHPXBV"><b>online</b></a>
\ No newline at end of file
+<a target="_blank" href="http://melpon.org/wandbox/permlink/cCQRCvjXdRM9YpT5"><b>online</b></a>
\ No newline at end of file
diff --git a/doc/examples/basic_json__object_t.link b/doc/examples/basic_json__object_t.link
index f6bad66..2e07a3e 100644
--- a/doc/examples/basic_json__object_t.link
+++ b/doc/examples/basic_json__object_t.link
@@ -1 +1 @@
-<a target="_blank" href="http://melpon.org/wandbox/permlink/YhDZ1WhldMdwBthI"><b>online</b></a>
\ No newline at end of file
+<a target="_blank" href="http://melpon.org/wandbox/permlink/DgtHcz5L6JphTOGJ"><b>online</b></a>
\ No newline at end of file
diff --git a/doc/examples/basic_json__size_type_basic_json.link b/doc/examples/basic_json__size_type_basic_json.link
index 6ae76b6..78305dc 100644
--- a/doc/examples/basic_json__size_type_basic_json.link
+++ b/doc/examples/basic_json__size_type_basic_json.link
@@ -1 +1 @@
-<a target="_blank" href="http://melpon.org/wandbox/permlink/OTJ7W8jL7z9qQlkL"><b>online</b></a>
\ No newline at end of file
+<a target="_blank" href="http://melpon.org/wandbox/permlink/IFZT4VL0oRotJBxl"><b>online</b></a>
\ No newline at end of file
diff --git a/doc/examples/basic_json__string_t.link b/doc/examples/basic_json__string_t.link
index a5051e5..d7d02f2 100644
--- a/doc/examples/basic_json__string_t.link
+++ b/doc/examples/basic_json__string_t.link
@@ -1 +1 @@
-<a target="_blank" href="http://melpon.org/wandbox/permlink/ZRv2SsnLGdLzBhFO"><b>online</b></a>
\ No newline at end of file
+<a target="_blank" href="http://melpon.org/wandbox/permlink/cwNYP1q2mT8CFLTk"><b>online</b></a>
\ No newline at end of file
diff --git a/doc/examples/basic_json__string_t_value_type.link b/doc/examples/basic_json__string_t_value_type.link
index 4646b5b..5690876 100644
--- a/doc/examples/basic_json__string_t_value_type.link
+++ b/doc/examples/basic_json__string_t_value_type.link
@@ -1 +1 @@
-<a target="_blank" href="http://melpon.org/wandbox/permlink/xttjHgaxVqpfriXz"><b>online</b></a>
\ No newline at end of file
+<a target="_blank" href="http://melpon.org/wandbox/permlink/AtV4zVErfLwkileg"><b>online</b></a>
\ No newline at end of file
diff --git a/doc/examples/basic_json__value.link b/doc/examples/basic_json__value.link
index 452a1fe..27e1dde 100644
--- a/doc/examples/basic_json__value.link
+++ b/doc/examples/basic_json__value.link
@@ -1 +1 @@
-<a target="_blank" href="http://melpon.org/wandbox/permlink/ejZBeBBPEBmYsPFz"><b>online</b></a>
\ No newline at end of file
+<a target="_blank" href="http://melpon.org/wandbox/permlink/qdV5W2Lrljmjv4LG"><b>online</b></a>
\ No newline at end of file
diff --git a/doc/examples/basic_json__value_t.link b/doc/examples/basic_json__value_t.link
index 774aaa2..e7e744c 100644
--- a/doc/examples/basic_json__value_t.link
+++ b/doc/examples/basic_json__value_t.link
@@ -1 +1 @@
-<a target="_blank" href="http://melpon.org/wandbox/permlink/fkrZveuulCw95PXF"><b>online</b></a>
\ No newline at end of file
+<a target="_blank" href="http://melpon.org/wandbox/permlink/RMpEI2o935KgD2o2"><b>online</b></a>
\ No newline at end of file
diff --git a/doc/examples/begin.link b/doc/examples/begin.link
index 6a76a58..64eac70 100644
--- a/doc/examples/begin.link
+++ b/doc/examples/begin.link
@@ -1 +1 @@
-<a target="_blank" href="http://melpon.org/wandbox/permlink/9HE53y23Ldu1xzQU"><b>online</b></a>
\ No newline at end of file
+<a target="_blank" href="http://melpon.org/wandbox/permlink/0lW1WJ1ZP8g92Jp9"><b>online</b></a>
\ No newline at end of file
diff --git a/doc/examples/cbegin.link b/doc/examples/cbegin.link
index 83c75dd..c298c65 100644
--- a/doc/examples/cbegin.link
+++ b/doc/examples/cbegin.link
@@ -1 +1 @@
-<a target="_blank" href="http://melpon.org/wandbox/permlink/8rGgyscFrA1fCiIn"><b>online</b></a>
\ No newline at end of file
+<a target="_blank" href="http://melpon.org/wandbox/permlink/yarD6DyxYlIf8Ymg"><b>online</b></a>
\ No newline at end of file
diff --git a/doc/examples/cend.link b/doc/examples/cend.link
index acca215..61a3203 100644
--- a/doc/examples/cend.link
+++ b/doc/examples/cend.link
@@ -1 +1 @@
-<a target="_blank" href="http://melpon.org/wandbox/permlink/Zek4QM4kvKLUywGC"><b>online</b></a>
\ No newline at end of file
+<a target="_blank" href="http://melpon.org/wandbox/permlink/9oPbpnFNgKtKjB0O"><b>online</b></a>
\ No newline at end of file
diff --git a/doc/examples/clear.link b/doc/examples/clear.link
index 8defa66..9ad0a14 100644
--- a/doc/examples/clear.link
+++ b/doc/examples/clear.link
@@ -1 +1 @@
-<a target="_blank" href="http://melpon.org/wandbox/permlink/D6RJMyjkibQmKUC4"><b>online</b></a>
\ No newline at end of file
+<a target="_blank" href="http://melpon.org/wandbox/permlink/wwsjMZP8U6bWhUil"><b>online</b></a>
\ No newline at end of file
diff --git a/doc/examples/count.link b/doc/examples/count.link
index e13caf3..62833ff 100644
--- a/doc/examples/count.link
+++ b/doc/examples/count.link
@@ -1 +1 @@
-<a target="_blank" href="http://melpon.org/wandbox/permlink/GFOaqTzQCZeTQk3R"><b>online</b></a>
\ No newline at end of file
+<a target="_blank" href="http://melpon.org/wandbox/permlink/XoXqF9LlWu8jlNgE"><b>online</b></a>
\ No newline at end of file
diff --git a/doc/examples/crbegin.link b/doc/examples/crbegin.link
index 27de8eb..1188be1 100644
--- a/doc/examples/crbegin.link
+++ b/doc/examples/crbegin.link
@@ -1 +1 @@
-<a target="_blank" href="http://melpon.org/wandbox/permlink/ZVFFzaSjRzx81O7o"><b>online</b></a>
\ No newline at end of file
+<a target="_blank" href="http://melpon.org/wandbox/permlink/GQz9A5y7HduQHVMw"><b>online</b></a>
\ No newline at end of file
diff --git a/doc/examples/crend.link b/doc/examples/crend.link
index c678236..4bdfef3 100644
--- a/doc/examples/crend.link
+++ b/doc/examples/crend.link
@@ -1 +1 @@
-<a target="_blank" href="http://melpon.org/wandbox/permlink/EmWt4iCaVbSmTxUs"><b>online</b></a>
\ No newline at end of file
+<a target="_blank" href="http://melpon.org/wandbox/permlink/Qom888gs2RM4wR4H"><b>online</b></a>
\ No newline at end of file
diff --git a/doc/examples/diff.cpp b/doc/examples/diff.cpp
new file mode 100644
index 0000000..d81a58d
--- /dev/null
+++ b/doc/examples/diff.cpp
@@ -0,0 +1,34 @@
+#include <json.hpp>
+
+using json = nlohmann::json;
+
+int main()
+{
+    // the source document
+    json source = R"(
+        {
+            "baz": "qux",
+            "foo": "bar"
+        }
+    )"_json;
+
+    // the target document
+    json target = R"(
+        {
+            "baz": "boo",
+            "hello": [
+                "world"
+            ]
+        }
+    )"_json;
+
+    // create the patch
+    json patch = json::diff(source, target);
+
+    // roundtrip
+    json patched_source = source.patch(patch);
+
+    // output patch and roundtrip result
+    std::cout << std::setw(4) << patch << "\n\n"
+              << std::setw(4) << patched_source << std::endl;
+}
diff --git a/doc/examples/diff.link b/doc/examples/diff.link
new file mode 100644
index 0000000..c3e3fa4
--- /dev/null
+++ b/doc/examples/diff.link
@@ -0,0 +1 @@
+<a target="_blank" href="http://melpon.org/wandbox/permlink/hicmeOK39tBxaluM"><b>online</b></a>
\ No newline at end of file
diff --git a/doc/examples/diff.output b/doc/examples/diff.output
new file mode 100644
index 0000000..7dc7979
--- /dev/null
+++ b/doc/examples/diff.output
@@ -0,0 +1,25 @@
+[
+    {
+        "op": "replace",
+        "path": "/baz",
+        "value": "boo"
+    },
+    {
+        "op": "remove",
+        "path": "/foo"
+    },
+    {
+        "op": "add",
+        "path": "/hello",
+        "value": [
+            "world"
+        ]
+    }
+]
+
+{
+    "baz": "boo",
+    "hello": [
+        "world"
+    ]
+}
diff --git a/doc/examples/dump.link b/doc/examples/dump.link
index 35ef465..5cf3e63 100644
--- a/doc/examples/dump.link
+++ b/doc/examples/dump.link
@@ -1 +1 @@
-<a target="_blank" href="http://melpon.org/wandbox/permlink/PjtHNsrmmgEeCFVp"><b>online</b></a>
\ No newline at end of file
+<a target="_blank" href="http://melpon.org/wandbox/permlink/rjYfsEq7v1RCHprk"><b>online</b></a>
\ No newline at end of file
diff --git a/doc/examples/empty.link b/doc/examples/empty.link
index fd22770..12aa967 100644
--- a/doc/examples/empty.link
+++ b/doc/examples/empty.link
@@ -1 +1 @@
-<a target="_blank" href="http://melpon.org/wandbox/permlink/daiGc21CilMsU7ln"><b>online</b></a>
\ No newline at end of file
+<a target="_blank" href="http://melpon.org/wandbox/permlink/rP64u8KqUJqB9lFd"><b>online</b></a>
\ No newline at end of file
diff --git a/doc/examples/end.link b/doc/examples/end.link
index 260696d..462a463 100644
--- a/doc/examples/end.link
+++ b/doc/examples/end.link
@@ -1 +1 @@
-<a target="_blank" href="http://melpon.org/wandbox/permlink/7QIAVTX9fvOkHUrH"><b>online</b></a>
\ No newline at end of file
+<a target="_blank" href="http://melpon.org/wandbox/permlink/4Fbmy4IG012HmGKs"><b>online</b></a>
\ No newline at end of file
diff --git a/doc/examples/erase__IteratorType.link b/doc/examples/erase__IteratorType.link
index b523066..854a9be 100644
--- a/doc/examples/erase__IteratorType.link
+++ b/doc/examples/erase__IteratorType.link
@@ -1 +1 @@
-<a target="_blank" href="http://melpon.org/wandbox/permlink/DlwzEWGJkvmikaNi"><b>online</b></a>
\ No newline at end of file
+<a target="_blank" href="http://melpon.org/wandbox/permlink/W83LsQNBy5qCoABq"><b>online</b></a>
\ No newline at end of file
diff --git a/doc/examples/erase__IteratorType_IteratorType.link b/doc/examples/erase__IteratorType_IteratorType.link
index b7f3da7..26e0b90 100644
--- a/doc/examples/erase__IteratorType_IteratorType.link
+++ b/doc/examples/erase__IteratorType_IteratorType.link
@@ -1 +1 @@
-<a target="_blank" href="http://melpon.org/wandbox/permlink/ds4TH3IAYVLCGyIz"><b>online</b></a>
\ No newline at end of file
+<a target="_blank" href="http://melpon.org/wandbox/permlink/zGMngigiwwhsXEhB"><b>online</b></a>
\ No newline at end of file
diff --git a/doc/examples/erase__key_type.link b/doc/examples/erase__key_type.link
index bef018c..e4c2fba 100644
--- a/doc/examples/erase__key_type.link
+++ b/doc/examples/erase__key_type.link
@@ -1 +1 @@
-<a target="_blank" href="http://melpon.org/wandbox/permlink/h6FfQqA0UIqdG6ca"><b>online</b></a>
\ No newline at end of file
+<a target="_blank" href="http://melpon.org/wandbox/permlink/0eXbiM5KTPPXFkyP"><b>online</b></a>
\ No newline at end of file
diff --git a/doc/examples/erase__size_type.link b/doc/examples/erase__size_type.link
index fe2d560..c63abe5 100644
--- a/doc/examples/erase__size_type.link
+++ b/doc/examples/erase__size_type.link
@@ -1 +1 @@
-<a target="_blank" href="http://melpon.org/wandbox/permlink/Fg7ut6CV4YBCsDbQ"><b>online</b></a>
\ No newline at end of file
+<a target="_blank" href="http://melpon.org/wandbox/permlink/WXGKRKjzP22ggTEr"><b>online</b></a>
\ No newline at end of file
diff --git a/doc/examples/find__key_type.link b/doc/examples/find__key_type.link
index 2bc0512..cd3d4fd 100644
--- a/doc/examples/find__key_type.link
+++ b/doc/examples/find__key_type.link
@@ -1 +1 @@
-<a target="_blank" href="http://melpon.org/wandbox/permlink/BfABW7AMVl4xqeoJ"><b>online</b></a>
\ No newline at end of file
+<a target="_blank" href="http://melpon.org/wandbox/permlink/Kk6nOpsa6KetydpZ"><b>online</b></a>
\ No newline at end of file
diff --git a/doc/examples/flatten.cpp b/doc/examples/flatten.cpp
new file mode 100644
index 0000000..ace53a2
--- /dev/null
+++ b/doc/examples/flatten.cpp
@@ -0,0 +1,30 @@
+#include <json.hpp>
+
+using json = nlohmann::json;
+
+int main()
+{
+    // create JSON value
+    json j =
+    {
+        {"pi", 3.141},
+        {"happy", true},
+        {"name", "Niels"},
+        {"nothing", nullptr},
+        {
+            "answer", {
+                {"everything", 42}
+            }
+        },
+        {"list", {1, 0, 2}},
+        {
+            "object", {
+                {"currency", "USD"},
+                {"value", 42.99}
+            }
+        }
+    };
+
+    // call flatten()
+    std::cout << std::setw(4) << j.flatten() << '\n';
+}
diff --git a/doc/examples/flatten.link b/doc/examples/flatten.link
new file mode 100644
index 0000000..50d3841
--- /dev/null
+++ b/doc/examples/flatten.link
@@ -0,0 +1 @@
+<a target="_blank" href="http://melpon.org/wandbox/permlink/cdQa6jE0N2LyqFJe"><b>online</b></a>
\ No newline at end of file
diff --git a/doc/examples/flatten.output b/doc/examples/flatten.output
new file mode 100644
index 0000000..33bd4c4
--- /dev/null
+++ b/doc/examples/flatten.output
@@ -0,0 +1,12 @@
+{
+    "/answer/everything": 42,
+    "/happy": true,
+    "/list/0": 1,
+    "/list/1": 0,
+    "/list/2": 2,
+    "/name": "Niels",
+    "/nothing": null,
+    "/object/currency": "USD",
+    "/object/value": 42.99,
+    "/pi": 3.141
+}
diff --git a/doc/examples/front.link b/doc/examples/front.link
index 22e99c7..d69c7f9 100644
--- a/doc/examples/front.link
+++ b/doc/examples/front.link
@@ -1 +1 @@
-<a target="_blank" href="http://melpon.org/wandbox/permlink/HheeceJWHngZFhu2"><b>online</b></a>
\ No newline at end of file
+<a target="_blank" href="http://melpon.org/wandbox/permlink/CUpMCzt6D9v3cYGM"><b>online</b></a>
\ No newline at end of file
diff --git a/doc/examples/get__PointerType.link b/doc/examples/get__PointerType.link
index a811b26..68a46b5 100644
--- a/doc/examples/get__PointerType.link
+++ b/doc/examples/get__PointerType.link
@@ -1 +1 @@
-<a target="_blank" href="http://melpon.org/wandbox/permlink/iunIxpTIIy6vcWJZ"><b>online</b></a>
\ No newline at end of file
+<a target="_blank" href="http://melpon.org/wandbox/permlink/TRkhQoCdScWQqi3x"><b>online</b></a>
\ No newline at end of file
diff --git a/doc/examples/get__ValueType_const.link b/doc/examples/get__ValueType_const.link
index fce774b..95862c6 100644
--- a/doc/examples/get__ValueType_const.link
+++ b/doc/examples/get__ValueType_const.link
@@ -1 +1 @@
-<a target="_blank" href="http://melpon.org/wandbox/permlink/7k9830oUxfGndWzT"><b>online</b></a>
\ No newline at end of file
+<a target="_blank" href="http://melpon.org/wandbox/permlink/ksZAkIKQnYhJKLpN"><b>online</b></a>
\ No newline at end of file
diff --git a/doc/examples/get_ptr.link b/doc/examples/get_ptr.link
index e318d77..13ed7d8 100644
--- a/doc/examples/get_ptr.link
+++ b/doc/examples/get_ptr.link
@@ -1 +1 @@
-<a target="_blank" href="http://melpon.org/wandbox/permlink/NDeUS78poIDrGhIP"><b>online</b></a>
\ No newline at end of file
+<a target="_blank" href="http://melpon.org/wandbox/permlink/xYbUV0BmOfNtQcED"><b>online</b></a>
\ No newline at end of file
diff --git a/doc/examples/get_ref.link b/doc/examples/get_ref.link
index c0d8275..ef560c4 100644
--- a/doc/examples/get_ref.link
+++ b/doc/examples/get_ref.link
@@ -1 +1 @@
-<a target="_blank" href="http://melpon.org/wandbox/permlink/5IINd4ZXCINhlk8u"><b>online</b></a>
\ No newline at end of file
+<a target="_blank" href="http://melpon.org/wandbox/permlink/WiO1oBWDvIs82OX1"><b>online</b></a>
\ No newline at end of file
diff --git a/doc/examples/insert.link b/doc/examples/insert.link
index 3886e04..9b0a1d9 100644
--- a/doc/examples/insert.link
+++ b/doc/examples/insert.link
@@ -1 +1 @@
-<a target="_blank" href="http://melpon.org/wandbox/permlink/814P5z0lA90nVh8b"><b>online</b></a>
\ No newline at end of file
+<a target="_blank" href="http://melpon.org/wandbox/permlink/ePN9gb4P3WTAiT6H"><b>online</b></a>
\ No newline at end of file
diff --git a/doc/examples/insert__count.link b/doc/examples/insert__count.link
index b97d0d6..f6a6441 100644
--- a/doc/examples/insert__count.link
+++ b/doc/examples/insert__count.link
@@ -1 +1 @@
-<a target="_blank" href="http://melpon.org/wandbox/permlink/OG3x3F7DFoqtHyqh"><b>online</b></a>
\ No newline at end of file
+<a target="_blank" href="http://melpon.org/wandbox/permlink/wnxritIWxpGS2SSt"><b>online</b></a>
\ No newline at end of file
diff --git a/doc/examples/insert__ilist.link b/doc/examples/insert__ilist.link
index 4920588..eba2b56 100644
--- a/doc/examples/insert__ilist.link
+++ b/doc/examples/insert__ilist.link
@@ -1 +1 @@
-<a target="_blank" href="http://melpon.org/wandbox/permlink/6gMuEfviD6ijflgd"><b>online</b></a>
\ No newline at end of file
+<a target="_blank" href="http://melpon.org/wandbox/permlink/kem0JnkdOVLNHii5"><b>online</b></a>
\ No newline at end of file
diff --git a/doc/examples/insert__range.link b/doc/examples/insert__range.link
index ac773f8..3a802b5 100644
--- a/doc/examples/insert__range.link
+++ b/doc/examples/insert__range.link
@@ -1 +1 @@
-<a target="_blank" href="http://melpon.org/wandbox/permlink/pUKVc892loGnjxUL"><b>online</b></a>
\ No newline at end of file
+<a target="_blank" href="http://melpon.org/wandbox/permlink/rub1CDmN5dKK1LJW"><b>online</b></a>
\ No newline at end of file
diff --git a/doc/examples/is_array.link b/doc/examples/is_array.link
index 8c8b9f8..ccd7275 100644
--- a/doc/examples/is_array.link
+++ b/doc/examples/is_array.link
@@ -1 +1 @@
-<a target="_blank" href="http://melpon.org/wandbox/permlink/0jJBJgku6NLIxJfv"><b>online</b></a>
\ No newline at end of file
+<a target="_blank" href="http://melpon.org/wandbox/permlink/hGGh0jnNmRo7bYAZ"><b>online</b></a>
\ No newline at end of file
diff --git a/doc/examples/is_boolean.link b/doc/examples/is_boolean.link
index 62f5fed..5ebc9ac 100644
--- a/doc/examples/is_boolean.link
+++ b/doc/examples/is_boolean.link
@@ -1 +1 @@
-<a target="_blank" href="http://melpon.org/wandbox/permlink/chWkSIROaePvwcDq"><b>online</b></a>
\ No newline at end of file
+<a target="_blank" href="http://melpon.org/wandbox/permlink/tWBJHygekWKqZIqe"><b>online</b></a>
\ No newline at end of file
diff --git a/doc/examples/is_discarded.link b/doc/examples/is_discarded.link
index 7ce69a1..d217239 100644
--- a/doc/examples/is_discarded.link
+++ b/doc/examples/is_discarded.link
@@ -1 +1 @@
-<a target="_blank" href="http://melpon.org/wandbox/permlink/j10gcmatdR8o625C"><b>online</b></a>
\ No newline at end of file
+<a target="_blank" href="http://melpon.org/wandbox/permlink/USx0486t9Qxi5LS3"><b>online</b></a>
\ No newline at end of file
diff --git a/doc/examples/is_null.link b/doc/examples/is_null.link
index 6b22b97..d40569d 100644
--- a/doc/examples/is_null.link
+++ b/doc/examples/is_null.link
@@ -1 +1 @@
-<a target="_blank" href="http://melpon.org/wandbox/permlink/rnEIZwD2AvtjaEzo"><b>online</b></a>
\ No newline at end of file
+<a target="_blank" href="http://melpon.org/wandbox/permlink/d75Hu1c8QVdDtabd"><b>online</b></a>
\ No newline at end of file
diff --git a/doc/examples/is_number.link b/doc/examples/is_number.link
index bf863a1..089d789 100644
--- a/doc/examples/is_number.link
+++ b/doc/examples/is_number.link
@@ -1 +1 @@
-<a target="_blank" href="http://melpon.org/wandbox/permlink/ZyMWRbD7gH5eC5Xy"><b>online</b></a>
\ No newline at end of file
+<a target="_blank" href="http://melpon.org/wandbox/permlink/97mIrpY9h1rodAfI"><b>online</b></a>
\ No newline at end of file
diff --git a/doc/examples/is_number_float.link b/doc/examples/is_number_float.link
index 38abb59..3fa7353 100644
--- a/doc/examples/is_number_float.link
+++ b/doc/examples/is_number_float.link
@@ -1 +1 @@
-<a target="_blank" href="http://melpon.org/wandbox/permlink/ouIIgi0X3Te9upBj"><b>online</b></a>
\ No newline at end of file
+<a target="_blank" href="http://melpon.org/wandbox/permlink/AHeVLaVJlG3cIehg"><b>online</b></a>
\ No newline at end of file
diff --git a/doc/examples/is_number_integer.link b/doc/examples/is_number_integer.link
index d6162ab..474cded 100644
--- a/doc/examples/is_number_integer.link
+++ b/doc/examples/is_number_integer.link
@@ -1 +1 @@
-<a target="_blank" href="http://melpon.org/wandbox/permlink/jZwJVIXretvfg4kS"><b>online</b></a>
\ No newline at end of file
+<a target="_blank" href="http://melpon.org/wandbox/permlink/WRiD854M5bTtQTnw"><b>online</b></a>
\ No newline at end of file
diff --git a/doc/examples/is_number_unsigned.link b/doc/examples/is_number_unsigned.link
index 41986ce..0849b8e 100644
--- a/doc/examples/is_number_unsigned.link
+++ b/doc/examples/is_number_unsigned.link
@@ -1 +1 @@
-<a target="_blank" href="http://melpon.org/wandbox/permlink/Vd3c1gtskokcJHp7"><b>online</b></a>
\ No newline at end of file
+<a target="_blank" href="http://melpon.org/wandbox/permlink/BYzDklHPHDZA2CXX"><b>online</b></a>
\ No newline at end of file
diff --git a/doc/examples/is_object.link b/doc/examples/is_object.link
index 1a304d0..5bd827c 100644
--- a/doc/examples/is_object.link
+++ b/doc/examples/is_object.link
@@ -1 +1 @@
-<a target="_blank" href="http://melpon.org/wandbox/permlink/ivLUeazmkk6aCUkE"><b>online</b></a>
\ No newline at end of file
+<a target="_blank" href="http://melpon.org/wandbox/permlink/TW8VagDAhVeaItcp"><b>online</b></a>
\ No newline at end of file
diff --git a/doc/examples/is_primitive.link b/doc/examples/is_primitive.link
index eb73ffb..6de65c7 100644
--- a/doc/examples/is_primitive.link
+++ b/doc/examples/is_primitive.link
@@ -1 +1 @@
-<a target="_blank" href="http://melpon.org/wandbox/permlink/9qjfclmdmZrCnVR3"><b>online</b></a>
\ No newline at end of file
+<a target="_blank" href="http://melpon.org/wandbox/permlink/A0B17yXywuT7Z1gx"><b>online</b></a>
\ No newline at end of file
diff --git a/doc/examples/is_string.link b/doc/examples/is_string.link
index 6af78b0..86ef2b4 100644
--- a/doc/examples/is_string.link
+++ b/doc/examples/is_string.link
@@ -1 +1 @@
-<a target="_blank" href="http://melpon.org/wandbox/permlink/Uh60XVyyG1N5pL2n"><b>online</b></a>
\ No newline at end of file
+<a target="_blank" href="http://melpon.org/wandbox/permlink/ePqrJBez1pH5dC7V"><b>online</b></a>
\ No newline at end of file
diff --git a/doc/examples/is_structured.link b/doc/examples/is_structured.link
index 8ae6fd0..43385c8 100644
--- a/doc/examples/is_structured.link
+++ b/doc/examples/is_structured.link
@@ -1 +1 @@
-<a target="_blank" href="http://melpon.org/wandbox/permlink/QH2PmyggbFbaA8Dg"><b>online</b></a>
\ No newline at end of file
+<a target="_blank" href="http://melpon.org/wandbox/permlink/46XRsxj3NvcwTpAZ"><b>online</b></a>
\ No newline at end of file
diff --git a/doc/examples/json_pointer.cpp b/doc/examples/json_pointer.cpp
new file mode 100644
index 0000000..140eac3
--- /dev/null
+++ b/doc/examples/json_pointer.cpp
@@ -0,0 +1,46 @@
+#include <json.hpp>
+
+using json = nlohmann::json;
+
+int main()
+{
+    // correct JSON pointers
+    json::json_pointer p1;
+    json::json_pointer p2("");
+    json::json_pointer p3("/");
+    json::json_pointer p4("//");
+    json::json_pointer p5("/foo/bar");
+    json::json_pointer p6("/foo/bar/-");
+    json::json_pointer p7("/foo/~0");
+    json::json_pointer p8("/foo/~1");
+
+    // error: JSON pointer does not begin with a slash
+    try
+    {
+        json::json_pointer p9("foo");
+    }
+    catch (std::domain_error& e)
+    {
+        std::cout << "domain_error: " << e.what() << '\n';
+    }
+
+    // error: JSON pointer uses escape symbol ~ not followed by 0 or 1
+    try
+    {
+        json::json_pointer p10("/foo/~");
+    }
+    catch (std::domain_error& e)
+    {
+        std::cout << "domain_error: " << e.what() << '\n';
+    }
+
+    // error: JSON pointer uses escape symbol ~ not followed by 0 or 1
+    try
+    {
+        json::json_pointer p11("/foo/~3");
+    }
+    catch (std::domain_error& e)
+    {
+        std::cout << "domain_error: " << e.what() << '\n';
+    }
+}
diff --git a/doc/examples/json_pointer.link b/doc/examples/json_pointer.link
new file mode 100644
index 0000000..6602f0c
--- /dev/null
+++ b/doc/examples/json_pointer.link
@@ -0,0 +1 @@
+<a target="_blank" href="http://melpon.org/wandbox/permlink/WM2WWKnXdmdw17Wu"><b>online</b></a>
\ No newline at end of file
diff --git a/doc/examples/json_pointer.output b/doc/examples/json_pointer.output
new file mode 100644
index 0000000..b81c8a2
--- /dev/null
+++ b/doc/examples/json_pointer.output
@@ -0,0 +1,3 @@
+domain_error: JSON pointer must be empty or begin with '/'
+domain_error: escape error: '~' must be followed with '0' or '1'
+domain_error: escape error: '~' must be followed with '0' or '1'
diff --git a/doc/examples/json_pointer__to_string.cpp b/doc/examples/json_pointer__to_string.cpp
new file mode 100644
index 0000000..4cb053c
--- /dev/null
+++ b/doc/examples/json_pointer__to_string.cpp
@@ -0,0 +1,34 @@
+#include <json.hpp>
+
+using json = nlohmann::json;
+
+int main()
+{
+    // different JSON Pointers
+    json::json_pointer ptr1("");
+    json::json_pointer ptr2("/foo");
+    json::json_pointer ptr3("/foo/0");
+    json::json_pointer ptr4("/");
+    json::json_pointer ptr5("/a~1b");
+    json::json_pointer ptr6("/c%d");
+    json::json_pointer ptr7("/e^f");
+    json::json_pointer ptr8("/g|h");
+    json::json_pointer ptr9("/i\\j");
+    json::json_pointer ptr10("/k\"l");
+    json::json_pointer ptr11("/ ");
+    json::json_pointer ptr12("/m~0n");
+
+
+    std::cout << ptr1.to_string() << '\n'
+              << ptr2.to_string() << '\n'
+              << ptr3.to_string() << '\n'
+              << ptr4.to_string() << '\n'
+              << ptr5.to_string() << '\n'
+              << ptr6.to_string() << '\n'
+              << ptr7.to_string() << '\n'
+              << ptr8.to_string() << '\n'
+              << ptr9.to_string() << '\n'
+              << ptr10.to_string() << '\n'
+              << ptr11.to_string() << '\n'
+              << ptr12.to_string() << std::endl;
+}
diff --git a/doc/examples/json_pointer__to_string.link b/doc/examples/json_pointer__to_string.link
new file mode 100644
index 0000000..407609b
--- /dev/null
+++ b/doc/examples/json_pointer__to_string.link
@@ -0,0 +1 @@
+<a target="_blank" href="http://melpon.org/wandbox/permlink/O4FbKx0TbZioFhfU"><b>online</b></a>
\ No newline at end of file
diff --git a/doc/examples/json_pointer__to_string.output b/doc/examples/json_pointer__to_string.output
new file mode 100644
index 0000000..c4b5ea8
--- /dev/null
+++ b/doc/examples/json_pointer__to_string.output
@@ -0,0 +1,12 @@
+
+/foo
+/foo/0
+/
+/a~1b
+/c%d
+/e^f
+/g|h
+/i\j
+/k"l
+/ 
+/m~0n
diff --git a/doc/examples/max_size.link b/doc/examples/max_size.link
index d91bc8c..5719cca 100644
--- a/doc/examples/max_size.link
+++ b/doc/examples/max_size.link
@@ -1 +1 @@
-<a target="_blank" href="http://melpon.org/wandbox/permlink/pHK8lxPJaR0LZwRM"><b>online</b></a>
\ No newline at end of file
+<a target="_blank" href="http://melpon.org/wandbox/permlink/NBFk3TxTo32pKt36"><b>online</b></a>
\ No newline at end of file
diff --git a/doc/examples/object.link b/doc/examples/object.link
index 575473b..fd0f424 100644
--- a/doc/examples/object.link
+++ b/doc/examples/object.link
@@ -1 +1 @@
-<a target="_blank" href="http://melpon.org/wandbox/permlink/g46YJ0TCAELXYvZb"><b>online</b></a>
\ No newline at end of file
+<a target="_blank" href="http://melpon.org/wandbox/permlink/hhxRaUctq3FA54SW"><b>online</b></a>
\ No newline at end of file
diff --git a/doc/examples/operator__ValueType.link b/doc/examples/operator__ValueType.link
index 204d8f5..1428a67 100644
--- a/doc/examples/operator__ValueType.link
+++ b/doc/examples/operator__ValueType.link
@@ -1 +1 @@
-<a target="_blank" href="http://melpon.org/wandbox/permlink/laFGVm1oHwhEIiJl"><b>online</b></a>
\ No newline at end of file
+<a target="_blank" href="http://melpon.org/wandbox/permlink/rUGX6AaVuZfwiiYI"><b>online</b></a>
\ No newline at end of file
diff --git a/doc/examples/operator__equal.link b/doc/examples/operator__equal.link
index 41ae20c..8670752 100644
--- a/doc/examples/operator__equal.link
+++ b/doc/examples/operator__equal.link
@@ -1 +1 @@
-<a target="_blank" href="http://melpon.org/wandbox/permlink/Zpc5xccVHh210pJ4"><b>online</b></a>
\ No newline at end of file
+<a target="_blank" href="http://melpon.org/wandbox/permlink/sBTv52u9kI2gjeFJ"><b>online</b></a>
\ No newline at end of file
diff --git a/doc/examples/operator__equal.output b/doc/examples/operator__equal.output
index 7806735..e9dfd75 100644
--- a/doc/examples/operator__equal.output
+++ b/doc/examples/operator__equal.output
@@ -1,4 +1,4 @@
 [1,2,3] == [1,2,4] false
 {"A":"a","B":"b"} == {"A":"a","B":"b"} true
-17 == 17.0 true
+17 == 17 true
 "foo" == "bar" false
diff --git a/doc/examples/operator__equal.test b/doc/examples/operator__equal.test
new file mode 100644
index 0000000..e9dfd75
--- /dev/null
+++ b/doc/examples/operator__equal.test
@@ -0,0 +1,4 @@
+[1,2,3] == [1,2,4] false
+{"A":"a","B":"b"} == {"A":"a","B":"b"} true
+17 == 17 true
+"foo" == "bar" false
diff --git a/doc/examples/operator__equal__nullptr_t.link b/doc/examples/operator__equal__nullptr_t.link
index 49e46fd..d52acab 100644
--- a/doc/examples/operator__equal__nullptr_t.link
+++ b/doc/examples/operator__equal__nullptr_t.link
@@ -1 +1 @@
-<a target="_blank" href="http://melpon.org/wandbox/permlink/Y7k4oKr0qq2C1m6Q"><b>online</b></a>
\ No newline at end of file
+<a target="_blank" href="http://melpon.org/wandbox/permlink/GGWi3cRmA5A3n1N2"><b>online</b></a>
\ No newline at end of file
diff --git a/doc/examples/operator__greater.link b/doc/examples/operator__greater.link
index 2299d61..5499771 100644
--- a/doc/examples/operator__greater.link
+++ b/doc/examples/operator__greater.link
@@ -1 +1 @@
-<a target="_blank" href="http://melpon.org/wandbox/permlink/FsDUutWOyZiFDADV"><b>online</b></a>
\ No newline at end of file
+<a target="_blank" href="http://melpon.org/wandbox/permlink/xJlXYtWnxO1NUh7x"><b>online</b></a>
\ No newline at end of file
diff --git a/doc/examples/operator__greaterequal.link b/doc/examples/operator__greaterequal.link
index fdd676e..e476f39 100644
--- a/doc/examples/operator__greaterequal.link
+++ b/doc/examples/operator__greaterequal.link
@@ -1 +1 @@
-<a target="_blank" href="http://melpon.org/wandbox/permlink/EGB8bgj4XZ1QDdoG"><b>online</b></a>
\ No newline at end of file
+<a target="_blank" href="http://melpon.org/wandbox/permlink/92RTw1dWSVue58j6"><b>online</b></a>
\ No newline at end of file
diff --git a/doc/examples/operator__less.link b/doc/examples/operator__less.link
index 5d7f6f5..d50c220 100644
--- a/doc/examples/operator__less.link
+++ b/doc/examples/operator__less.link
@@ -1 +1 @@
-<a target="_blank" href="http://melpon.org/wandbox/permlink/4UPUe8lpV9e48paC"><b>online</b></a>
\ No newline at end of file
+<a target="_blank" href="http://melpon.org/wandbox/permlink/JrAvlrYH03a2yfRX"><b>online</b></a>
\ No newline at end of file
diff --git a/doc/examples/operator__lessequal.link b/doc/examples/operator__lessequal.link
index 620b6aa..3b71a46 100644
--- a/doc/examples/operator__lessequal.link
+++ b/doc/examples/operator__lessequal.link
@@ -1 +1 @@
-<a target="_blank" href="http://melpon.org/wandbox/permlink/D1CUYI3AqEozXpjh"><b>online</b></a>
\ No newline at end of file
+<a target="_blank" href="http://melpon.org/wandbox/permlink/m4lNMI48FqF96QWd"><b>online</b></a>
\ No newline at end of file
diff --git a/doc/examples/operator__notequal.link b/doc/examples/operator__notequal.link
index 482b972..49f1529 100644
--- a/doc/examples/operator__notequal.link
+++ b/doc/examples/operator__notequal.link
@@ -1 +1 @@
-<a target="_blank" href="http://melpon.org/wandbox/permlink/AVJ7PQO6te3sC6kp"><b>online</b></a>
\ No newline at end of file
+<a target="_blank" href="http://melpon.org/wandbox/permlink/m4adEpqac1fmOwNn"><b>online</b></a>
\ No newline at end of file
diff --git a/doc/examples/operator__notequal.output b/doc/examples/operator__notequal.output
index 9eba626..ddd838b 100644
--- a/doc/examples/operator__notequal.output
+++ b/doc/examples/operator__notequal.output
@@ -1,4 +1,4 @@
 [1,2,3] == [1,2,4] true
 {"A":"a","B":"b"} == {"A":"a","B":"b"} false
-17 == 17.0 false
+17 == 17 false
 "foo" == "bar" true
diff --git a/doc/examples/operator__notequal.test b/doc/examples/operator__notequal.test
new file mode 100644
index 0000000..ddd838b
--- /dev/null
+++ b/doc/examples/operator__notequal.test
@@ -0,0 +1,4 @@
+[1,2,3] == [1,2,4] true
+{"A":"a","B":"b"} == {"A":"a","B":"b"} false
+17 == 17 false
+"foo" == "bar" true
diff --git a/doc/examples/operator__notequal__nullptr_t.link b/doc/examples/operator__notequal__nullptr_t.link
index 40f6bb4..2103362 100644
--- a/doc/examples/operator__notequal__nullptr_t.link
+++ b/doc/examples/operator__notequal__nullptr_t.link
@@ -1 +1 @@
-<a target="_blank" href="http://melpon.org/wandbox/permlink/E0Hu2nk9YvMUW1ck"><b>online</b></a>
\ No newline at end of file
+<a target="_blank" href="http://melpon.org/wandbox/permlink/Lo7eftLny4Io5rgV"><b>online</b></a>
\ No newline at end of file
diff --git a/doc/examples/operator__value_t.link b/doc/examples/operator__value_t.link
index a970723..3139efa 100644
--- a/doc/examples/operator__value_t.link
+++ b/doc/examples/operator__value_t.link
@@ -1 +1 @@
-<a target="_blank" href="http://melpon.org/wandbox/permlink/qt8t1mE1LjXjtBtO"><b>online</b></a>
\ No newline at end of file
+<a target="_blank" href="http://melpon.org/wandbox/permlink/n1XysfgD6muGNQdE"><b>online</b></a>
\ No newline at end of file
diff --git a/doc/examples/operator_deserialize.link b/doc/examples/operator_deserialize.link
index 27e998d..fea1503 100644
--- a/doc/examples/operator_deserialize.link
+++ b/doc/examples/operator_deserialize.link
@@ -1 +1 @@
-<a target="_blank" href="http://melpon.org/wandbox/permlink/Q4f0uwIQWvnheKCb"><b>online</b></a>
\ No newline at end of file
+<a target="_blank" href="http://melpon.org/wandbox/permlink/yAAGR07u3IQbIgUa"><b>online</b></a>
\ No newline at end of file
diff --git a/doc/examples/operator_serialize.link b/doc/examples/operator_serialize.link
index 231306a..45b5f4b 100644
--- a/doc/examples/operator_serialize.link
+++ b/doc/examples/operator_serialize.link
@@ -1 +1 @@
-<a target="_blank" href="http://melpon.org/wandbox/permlink/c5XBX38eOBEz5Xkw"><b>online</b></a>
\ No newline at end of file
+<a target="_blank" href="http://melpon.org/wandbox/permlink/NOtGXsCLXYHRG6ML"><b>online</b></a>
\ No newline at end of file
diff --git a/doc/examples/operatorarray__key_type.link b/doc/examples/operatorarray__key_type.link
index 40df071..d597ad7 100644
--- a/doc/examples/operatorarray__key_type.link
+++ b/doc/examples/operatorarray__key_type.link
@@ -1 +1 @@
-<a target="_blank" href="http://melpon.org/wandbox/permlink/ibp0eDm8dPhHEsSB"><b>online</b></a>
\ No newline at end of file
+<a target="_blank" href="http://melpon.org/wandbox/permlink/ujLOsxCRQE3T8cYO"><b>online</b></a>
\ No newline at end of file
diff --git a/doc/examples/operatorarray__key_type_const.link b/doc/examples/operatorarray__key_type_const.link
index bba664a..d34adb4 100644
--- a/doc/examples/operatorarray__key_type_const.link
+++ b/doc/examples/operatorarray__key_type_const.link
@@ -1 +1 @@
-<a target="_blank" href="http://melpon.org/wandbox/permlink/pHB8PKHjuFOesfMM"><b>online</b></a>
\ No newline at end of file
+<a target="_blank" href="http://melpon.org/wandbox/permlink/x6rzLLCk96NM36Tl"><b>online</b></a>
\ No newline at end of file
diff --git a/doc/examples/operatorarray__size_type.link b/doc/examples/operatorarray__size_type.link
index b9d5788..aa5f718 100644
--- a/doc/examples/operatorarray__size_type.link
+++ b/doc/examples/operatorarray__size_type.link
@@ -1 +1 @@
-<a target="_blank" href="http://melpon.org/wandbox/permlink/KUCir2gm6mh39HNy"><b>online</b></a>
\ No newline at end of file
+<a target="_blank" href="http://melpon.org/wandbox/permlink/nRWrbGoBCORCHJI5"><b>online</b></a>
\ No newline at end of file
diff --git a/doc/examples/operatorarray__size_type_const.link b/doc/examples/operatorarray__size_type_const.link
index a1f8c98..a5c351e 100644
--- a/doc/examples/operatorarray__size_type_const.link
+++ b/doc/examples/operatorarray__size_type_const.link
@@ -1 +1 @@
-<a target="_blank" href="http://melpon.org/wandbox/permlink/KGERZ2Wgb9dpt4V0"><b>online</b></a>
\ No newline at end of file
+<a target="_blank" href="http://melpon.org/wandbox/permlink/4g1Gn5htCIExLBgW"><b>online</b></a>
\ No newline at end of file
diff --git a/doc/examples/operatorjson_pointer.cpp b/doc/examples/operatorjson_pointer.cpp
new file mode 100644
index 0000000..18e41c1
--- /dev/null
+++ b/doc/examples/operatorjson_pointer.cpp
@@ -0,0 +1,47 @@
+#include <json.hpp>
+
+using json = nlohmann::json;
+
+int main()
+{
+    // create a JSON value
+    json j =
+    {
+        {"number", 1}, {"string", "foo"}, {"array", {1, 2}}
+    };
+
+    // read-only access
+
+    // output element with JSON pointer "/number"
+    std::cout << j["/number"_json_pointer] << '\n';
+    // output element with JSON pointer "/string"
+    std::cout << j["/string"_json_pointer] << '\n';
+    // output element with JSON pointer "/array"
+    std::cout << j["/array"_json_pointer] << '\n';
+    // output element with JSON pointer "/array/1"
+    std::cout << j["/array/1"_json_pointer] << '\n';
+
+    // writing access
+
+    // change the string
+    j["/string"_json_pointer] = "bar";
+    // output the changed string
+    std::cout << j["string"] << '\n';
+
+    // "change" a nonexisting object entry
+    j["/boolean"_json_pointer] = true;
+    // output the changed object
+    std::cout << j << '\n';
+
+    // change an array element
+    j["/array/1"_json_pointer] = 21;
+    // "change" an array element with nonexisting index
+    j["/array/4"_json_pointer] = 44;
+    // output the changed array
+    std::cout << j["array"] << '\n';
+
+    // "change" the arry element past the end
+    j["/array/-"_json_pointer] = 55;
+    // output the changed array
+    std::cout << j["array"] << '\n';
+}
diff --git a/doc/examples/operatorjson_pointer.link b/doc/examples/operatorjson_pointer.link
new file mode 100644
index 0000000..3a60e8a
--- /dev/null
+++ b/doc/examples/operatorjson_pointer.link
@@ -0,0 +1 @@
+<a target="_blank" href="http://melpon.org/wandbox/permlink/xzFX3E4gYBXFwd04"><b>online</b></a>
\ No newline at end of file
diff --git a/doc/examples/operatorjson_pointer.output b/doc/examples/operatorjson_pointer.output
new file mode 100644
index 0000000..1fd1b03
--- /dev/null
+++ b/doc/examples/operatorjson_pointer.output
@@ -0,0 +1,8 @@
+1
+"foo"
+[1,2]
+2
+"bar"
+{"array":[1,2],"boolean":true,"number":1,"string":"bar"}
+[1,21,null,null,44]
+[1,21,null,null,44,55]
diff --git a/doc/examples/operatorjson_pointer_const.cpp b/doc/examples/operatorjson_pointer_const.cpp
new file mode 100644
index 0000000..20ac36c
--- /dev/null
+++ b/doc/examples/operatorjson_pointer_const.cpp
@@ -0,0 +1,23 @@
+#include <json.hpp>
+
+using json = nlohmann::json;
+
+int main()
+{
+    // create a JSON value
+    const json j =
+    {
+        {"number", 1}, {"string", "foo"}, {"array", {1, 2}}
+    };
+
+    // read-only access
+
+    // output element with JSON pointer "/number"
+    std::cout << j["/number"_json_pointer] << '\n';
+    // output element with JSON pointer "/string"
+    std::cout << j["/string"_json_pointer] << '\n';
+    // output element with JSON pointer "/array"
+    std::cout << j["/array"_json_pointer] << '\n';
+    // output element with JSON pointer "/array/1"
+    std::cout << j["/array/1"_json_pointer] << '\n';
+}
diff --git a/doc/examples/operatorjson_pointer_const.link b/doc/examples/operatorjson_pointer_const.link
new file mode 100644
index 0000000..1bc1423
--- /dev/null
+++ b/doc/examples/operatorjson_pointer_const.link
@@ -0,0 +1 @@
+<a target="_blank" href="http://melpon.org/wandbox/permlink/IKpbJkMdcocVdlRd"><b>online</b></a>
\ No newline at end of file
diff --git a/doc/examples/operatorjson_pointer_const.output b/doc/examples/operatorjson_pointer_const.output
new file mode 100644
index 0000000..7b9306b
--- /dev/null
+++ b/doc/examples/operatorjson_pointer_const.output
@@ -0,0 +1,4 @@
+1
+"foo"
+[1,2]
+2
diff --git a/doc/examples/parse__istream__parser_callback_t.link b/doc/examples/parse__istream__parser_callback_t.link
index 85960e1..4d2d0a9 100644
--- a/doc/examples/parse__istream__parser_callback_t.link
+++ b/doc/examples/parse__istream__parser_callback_t.link
@@ -1 +1 @@
-<a target="_blank" href="http://melpon.org/wandbox/permlink/9Maolv5LJQM9pbqb"><b>online</b></a>
\ No newline at end of file
+<a target="_blank" href="http://melpon.org/wandbox/permlink/ir2u6Qbw18O89IWk"><b>online</b></a>
\ No newline at end of file
diff --git a/doc/examples/parse__string__parser_callback_t.link b/doc/examples/parse__string__parser_callback_t.link
index 9db1c3b..1ad3b71 100644
--- a/doc/examples/parse__string__parser_callback_t.link
+++ b/doc/examples/parse__string__parser_callback_t.link
@@ -1 +1 @@
-<a target="_blank" href="http://melpon.org/wandbox/permlink/rleJPtEaWVJ9oBAn"><b>online</b></a>
\ No newline at end of file
+<a target="_blank" href="http://melpon.org/wandbox/permlink/SrKpkE9ivmvd2OUy"><b>online</b></a>
\ No newline at end of file
diff --git a/doc/examples/patch.cpp b/doc/examples/patch.cpp
new file mode 100644
index 0000000..24a52d5
--- /dev/null
+++ b/doc/examples/patch.cpp
@@ -0,0 +1,30 @@
+#include <json.hpp>
+
+using json = nlohmann::json;
+
+int main()
+{
+    // the original document
+    json doc = R"(
+        {
+          "baz": "qux",
+          "foo": "bar"
+        }
+    )"_json;
+
+    // the patch
+    json patch = R"(
+        [
+          { "op": "replace", "path": "/baz", "value": "boo" },
+          { "op": "add", "path": "/hello", "value": ["world"] },
+          { "op": "remove", "path": "/foo"}
+        ]
+    )"_json;
+
+    // apply the patch
+    json patched_doc = doc.patch(patch);
+
+    // output original and patched document
+    std::cout << std::setw(4) << doc << "\n\n"
+              << std::setw(4) << patched_doc << std::endl;
+}
diff --git a/doc/examples/patch.link b/doc/examples/patch.link
new file mode 100644
index 0000000..5d5032b
--- /dev/null
+++ b/doc/examples/patch.link
@@ -0,0 +1 @@
+<a target="_blank" href="http://melpon.org/wandbox/permlink/lbczW3AzcUbH1Nbo"><b>online</b></a>
\ No newline at end of file
diff --git a/doc/examples/patch.output b/doc/examples/patch.output
new file mode 100644
index 0000000..eb558fe
--- /dev/null
+++ b/doc/examples/patch.output
@@ -0,0 +1,11 @@
+{
+    "baz": "qux",
+    "foo": "bar"
+}
+
+{
+    "baz": "boo",
+    "hello": [
+        "world"
+    ]
+}
diff --git a/doc/examples/push_back.link b/doc/examples/push_back.link
index 8ec17db..d0ca11b 100644
--- a/doc/examples/push_back.link
+++ b/doc/examples/push_back.link
@@ -1 +1 @@
-<a target="_blank" href="http://melpon.org/wandbox/permlink/NDbzJ3aIWcJCtyQj"><b>online</b></a>
\ No newline at end of file
+<a target="_blank" href="http://melpon.org/wandbox/permlink/zz3xkIXJYVhmYAla"><b>online</b></a>
\ No newline at end of file
diff --git a/doc/examples/push_back__initializer_list.cpp b/doc/examples/push_back__initializer_list.cpp
new file mode 100644
index 0000000..9fe01ad
--- /dev/null
+++ b/doc/examples/push_back__initializer_list.cpp
@@ -0,0 +1,26 @@
+#include <json.hpp>
+
+using json = nlohmann::json;
+
+int main()
+{
+    // create JSON values
+    json object = {{"one", 1}, {"two", 2}};
+    json null;
+
+    // print values
+    std::cout << object << '\n';
+    std::cout << null << '\n';
+
+    // add values:
+    object.push_back({"three", 3});  // object is extended
+    object += {"four", 4};           // object is extended
+    null.push_back({"five", 5});     // null is converted to array
+
+    // print values
+    std::cout << object << '\n';
+    std::cout << null << '\n';
+
+    // would throw:
+    //object.push_back({1, 2, 3});
+}
diff --git a/doc/examples/push_back__initializer_list.link b/doc/examples/push_back__initializer_list.link
new file mode 100644
index 0000000..2b5abae
--- /dev/null
+++ b/doc/examples/push_back__initializer_list.link
@@ -0,0 +1 @@
+<a target="_blank" href="http://melpon.org/wandbox/permlink/wZF4dRHjfCyjb3rx"><b>online</b></a>
\ No newline at end of file
diff --git a/doc/examples/push_back__initializer_list.output b/doc/examples/push_back__initializer_list.output
new file mode 100644
index 0000000..668eb25
--- /dev/null
+++ b/doc/examples/push_back__initializer_list.output
@@ -0,0 +1,4 @@
+{"one":1,"two":2}
+null
+{"four":4,"one":1,"three":3,"two":2}
+[["five",5]]
diff --git a/doc/examples/push_back__object_t__value.link b/doc/examples/push_back__object_t__value.link
index 24882e3..04bdbd9 100644
--- a/doc/examples/push_back__object_t__value.link
+++ b/doc/examples/push_back__object_t__value.link
@@ -1 +1 @@
-<a target="_blank" href="http://melpon.org/wandbox/permlink/PbG1Ox3wbWO5M2n0"><b>online</b></a>
\ No newline at end of file
+<a target="_blank" href="http://melpon.org/wandbox/permlink/lmXOd27kgJ2wQGMZ"><b>online</b></a>
\ No newline at end of file
diff --git a/doc/examples/rbegin.link b/doc/examples/rbegin.link
index de269d7..79ed80c 100644
--- a/doc/examples/rbegin.link
+++ b/doc/examples/rbegin.link
@@ -1 +1 @@
-<a target="_blank" href="http://melpon.org/wandbox/permlink/61XbPi0WzoFMwGOC"><b>online</b></a>
\ No newline at end of file
+<a target="_blank" href="http://melpon.org/wandbox/permlink/KiWAz9ZSK8L3k2kg"><b>online</b></a>
\ No newline at end of file
diff --git a/doc/examples/rend.link b/doc/examples/rend.link
index 17faa91..6e8850f 100644
--- a/doc/examples/rend.link
+++ b/doc/examples/rend.link
@@ -1 +1 @@
-<a target="_blank" href="http://melpon.org/wandbox/permlink/1jdSpDY9sGkVXNXf"><b>online</b></a>
\ No newline at end of file
+<a target="_blank" href="http://melpon.org/wandbox/permlink/2QoIzXRq9ODTMxRf"><b>online</b></a>
\ No newline at end of file
diff --git a/doc/examples/size.link b/doc/examples/size.link
index 4cc0afa..9b54619 100644
--- a/doc/examples/size.link
+++ b/doc/examples/size.link
@@ -1 +1 @@
-<a target="_blank" href="http://melpon.org/wandbox/permlink/EbP2syhcIlnzaVgg"><b>online</b></a>
\ No newline at end of file
+<a target="_blank" href="http://melpon.org/wandbox/permlink/ykuhQopCVvOXcvs9"><b>online</b></a>
\ No newline at end of file
diff --git a/doc/examples/swap__array_t.link b/doc/examples/swap__array_t.link
index 8851fdb..1dca248 100644
--- a/doc/examples/swap__array_t.link
+++ b/doc/examples/swap__array_t.link
@@ -1 +1 @@
-<a target="_blank" href="http://melpon.org/wandbox/permlink/Dm8mklPRNsWM9jDG"><b>online</b></a>
\ No newline at end of file
+<a target="_blank" href="http://melpon.org/wandbox/permlink/Hb0oakHEKJ2cuMTE"><b>online</b></a>
\ No newline at end of file
diff --git a/doc/examples/swap__object_t.link b/doc/examples/swap__object_t.link
index 8bd7495..cdb94e9 100644
--- a/doc/examples/swap__object_t.link
+++ b/doc/examples/swap__object_t.link
@@ -1 +1 @@
-<a target="_blank" href="http://melpon.org/wandbox/permlink/oTEHCv1zOE5n03v8"><b>online</b></a>
\ No newline at end of file
+<a target="_blank" href="http://melpon.org/wandbox/permlink/flPVbr8widwRmnad"><b>online</b></a>
\ No newline at end of file
diff --git a/doc/examples/swap__reference.link b/doc/examples/swap__reference.link
index 3ee4c0d..262d196 100644
--- a/doc/examples/swap__reference.link
+++ b/doc/examples/swap__reference.link
@@ -1 +1 @@
-<a target="_blank" href="http://melpon.org/wandbox/permlink/HUBPhdgANUGiBEWp"><b>online</b></a>
\ No newline at end of file
+<a target="_blank" href="http://melpon.org/wandbox/permlink/gYuvmciucjS8NYY0"><b>online</b></a>
\ No newline at end of file
diff --git a/doc/examples/swap__string_t.link b/doc/examples/swap__string_t.link
index 7193136..f3c448d 100644
--- a/doc/examples/swap__string_t.link
+++ b/doc/examples/swap__string_t.link
@@ -1 +1 @@
-<a target="_blank" href="http://melpon.org/wandbox/permlink/BBJAr8uCd2ru1ODN"><b>online</b></a>
\ No newline at end of file
+<a target="_blank" href="http://melpon.org/wandbox/permlink/747DNDuWilHBrac3"><b>online</b></a>
\ No newline at end of file
diff --git a/doc/examples/type.link b/doc/examples/type.link
index f197d84..ccb4422 100644
--- a/doc/examples/type.link
+++ b/doc/examples/type.link
@@ -1 +1 @@
-<a target="_blank" href="http://melpon.org/wandbox/permlink/ZfaJtrNZiwyke1M8"><b>online</b></a>
\ No newline at end of file
+<a target="_blank" href="http://melpon.org/wandbox/permlink/ZqSWHApgHNYddvEU"><b>online</b></a>
\ No newline at end of file
diff --git a/doc/examples/unflatten.cpp b/doc/examples/unflatten.cpp
new file mode 100644
index 0000000..e2b9b6b
--- /dev/null
+++ b/doc/examples/unflatten.cpp
@@ -0,0 +1,24 @@
+#include <json.hpp>
+
+using json = nlohmann::json;
+
+int main()
+{
+    // create JSON value
+    json j_flattened =
+    {
+        {"/answer/everything", 42},
+        {"/happy", true},
+        {"/list/0", 1},
+        {"/list/1", 0},
+        {"/list/2", 2},
+        {"/name", "Niels"},
+        {"/nothing", nullptr},
+        {"/object/currency", "USD"},
+        {"/object/value", 42.99},
+        {"/pi", 3.141}
+    };
+
+    // call unflatten()
+    std::cout << std::setw(4) << j_flattened.unflatten() << '\n';
+}
diff --git a/doc/examples/unflatten.link b/doc/examples/unflatten.link
new file mode 100644
index 0000000..386f5e1
--- /dev/null
+++ b/doc/examples/unflatten.link
@@ -0,0 +1 @@
+<a target="_blank" href="http://melpon.org/wandbox/permlink/PFBoQVT7RG0e0Wua"><b>online</b></a>
\ No newline at end of file
diff --git a/doc/examples/unflatten.output b/doc/examples/unflatten.output
new file mode 100644
index 0000000..ed48385
--- /dev/null
+++ b/doc/examples/unflatten.output
@@ -0,0 +1,18 @@
+{
+    "answer": {
+        "everything": 42
+    },
+    "happy": true,
+    "list": [
+        1,
+        0,
+        2
+    ],
+    "name": "Niels",
+    "nothing": null,
+    "object": {
+        "currency": "USD",
+        "value": 42.99
+    },
+    "pi": 3.141
+}
diff --git a/doc/json.gif b/doc/json.gif
index 7cd9437..7edafe0 100644
--- a/doc/json.gif
+++ b/doc/json.gif
Binary files differ
diff --git a/src/json.hpp b/src/json.hpp
index 8f671fb..b02a134 100644
--- a/src/json.hpp
+++ b/src/json.hpp
@@ -198,6 +198,9 @@
           AllocatorType>;
 
   public:
+    // forward declarations
+    template<typename Base> class json_reverse_iterator;
+    class json_pointer;
 
     /////////////////////
     // container types //
@@ -227,9 +230,6 @@
     /// the type of an element const pointer
     using const_pointer = typename std::allocator_traits<allocator_type>::const_pointer;
 
-    // forward declaration
-    template<typename Base> class json_reverse_iterator;
-
     /// an iterator for a basic_json container
     class iterator;
     /// a const iterator for a basic_json container
@@ -271,8 +271,8 @@
 
     @tparam ObjectType  the container to store objects (e.g., `std::map` or
     `std::unordered_map`)
-    @tparam StringType the type of the keys or names (e.g., `std::string`). The
-    comparison function `std::less<StringType>` is used to order elements
+    @tparam StringType the type of the keys or names (e.g., `std::string`).
+    The comparison function `std::less<StringType>` is used to order elements
     inside the container.
     @tparam AllocatorType the allocator to use for objects (e.g.,
     `std::allocator`)
@@ -280,8 +280,8 @@
     #### Default type
 
     With the default values for @a ObjectType (`std::map`), @a StringType
-    (`std::string`), and @a AllocatorType (`std::allocator`), the default value
-    for @a object_t is:
+    (`std::string`), and @a AllocatorType (`std::allocator`), the default
+    value for @a object_t is:
 
     @code {.cpp}
     std::map<
@@ -298,16 +298,16 @@
     the default type, objects have the following behavior:
 
     - When all names are unique, objects will be interoperable in the sense
-      that all software implementations receiving that object will agree on the
-      name-value mappings.
+      that all software implementations receiving that object will agree on
+      the name-value mappings.
     - When the names within an object are not unique, later stored name/value
       pairs overwrite previously stored name/value pairs, leaving the used
       names unique. For instance, `{"key": 1}` and `{"key": 2, "key": 1}` will
       be treated as equal and both stored as `{"key": 1}`.
     - Internally, name/value pairs are stored in lexicographical order of the
-      names. Objects will also be serialized (see @ref dump) in this order. For
-      instance, `{"b": 1, "a": 2}` and `{"a": 2, "b": 1}` will be stored and
-      serialized as `{"a": 2, "b": 1}`.
+      names. Objects will also be serialized (see @ref dump) in this order.
+      For instance, `{"b": 1, "a": 2}` and `{"a": 2, "b": 1}` will be stored
+      and serialized as `{"a": 2, "b": 1}`.
     - When comparing objects, the order of the name/value pairs is irrelevant.
       This makes objects interoperable in the sense that they will not be
       affected by these differences. For instance, `{"b": 1, "a": 2}` and
@@ -320,23 +320,24 @@
 
     In this class, the object's limit of nesting is not constraint explicitly.
     However, a maximum depth of nesting may be introduced by the compiler or
-    runtime environment. A theoretical limit can be queried by calling the @ref
-    max_size function of a JSON object.
+    runtime environment. A theoretical limit can be queried by calling the
+    @ref max_size function of a JSON object.
 
     #### Storage
 
     Objects are stored as pointers in a @ref basic_json type. That is, for any
-    access to object values, a pointer of type `object_t*` must be dereferenced.
+    access to object values, a pointer of type `object_t*` must be
+    dereferenced.
 
     @sa @ref array_t -- type for an array value
 
     @since version 1.0.0
 
-    @note The order name/value pairs are added to the object is *not* preserved
-    by the library. Therefore, iterating an object may return name/value pairs
-    in a different order than they were originally stored. In fact, keys will
-    be traversed in alphabetical order as `std::map` with `std::less` is used
-    by default. Please note this behavior conforms to [RFC
+    @note The order name/value pairs are added to the object is *not*
+    preserved by the library. Therefore, iterating an object may return
+    name/value pairs in a different order than they were originally stored. In
+    fact, keys will be traversed in alphabetical order as `std::map` with
+    `std::less` is used by default. Please note this behavior conforms to [RFC
     7159](http://rfc7159.net/rfc7159), because any order implements the
     specified "unordered" nature of JSON objects.
     */
@@ -378,8 +379,8 @@
 
     In this class, the array's limit of nesting is not constraint explicitly.
     However, a maximum depth of nesting may be introduced by the compiler or
-    runtime environment. A theoretical limit can be queried by calling the @ref
-    max_size function of a JSON array.
+    runtime environment. A theoretical limit can be queried by calling the
+    @ref max_size function of a JSON array.
 
     #### Storage
 
@@ -399,8 +400,8 @@
     > A string is a sequence of zero or more Unicode characters.
 
     To store objects in C++, a type is defined by the template parameter
-    described below. Unicode values are split by the JSON class into byte-sized
-    characters during deserialization.
+    described below. Unicode values are split by the JSON class into
+    byte-sized characters during deserialization.
 
     @tparam StringType  the container to store strings (e.g., `std::string`).
     Note this container is used for keys/names in objects, see @ref object_t.
@@ -469,19 +470,19 @@
     @brief a type for a number (integer)
 
     [RFC 7159](http://rfc7159.net/rfc7159) describes numbers as follows:
-    > The representation of numbers is similar to that used in most programming
-    > languages. A number is represented in base 10 using decimal digits. It
-    > contains an integer component that may be prefixed with an optional minus
-    > sign, which may be followed by a fraction part and/or an exponent part.
-    > Leading zeros are not allowed. (...) Numeric values that cannot be
-    > represented in the grammar below (such as Infinity and NaN) are not
-    > permitted.
+    > The representation of numbers is similar to that used in most
+    > programming languages. A number is represented in base 10 using decimal
+    > digits. It contains an integer component that may be prefixed with an
+    > optional minus sign, which may be followed by a fraction part and/or an
+    > exponent part. Leading zeros are not allowed. (...) Numeric values that
+    > cannot be represented in the grammar below (such as Infinity and NaN)
+    > are not permitted.
 
-    This description includes both integer and floating-point numbers. However,
-    C++ allows more precise storage if it is known whether the number is a
-    signed integer, an unsigned integer or a floating-point number. Therefore,
-    three different types, @ref number_integer_t, @ref number_unsigned_t and
-    @ref number_float_t are used.
+    This description includes both integer and floating-point numbers.
+    However, C++ allows more precise storage if it is known whether the number
+    is a signed integer, an unsigned integer or a floating-point number.
+    Therefore, three different types, @ref number_integer_t, @ref
+    number_unsigned_t and @ref number_float_t are used.
 
     To store integer numbers in C++, a type is defined by the template
     parameter @a NumberIntegerType which chooses the type to use.
@@ -500,8 +501,8 @@
     - The restrictions about leading zeros is not enforced in C++. Instead,
       leading zeros in integer literals lead to an interpretation as octal
       number. Internally, the value will be stored as decimal number. For
-      instance, the C++ integer literal `010` will be serialized to `8`. During
-      deserialization, leading zeros yield an error.
+      instance, the C++ integer literal `010` will be serialized to `8`.
+      During deserialization, leading zeros yield an error.
     - Not-a-number (NaN) values will be serialized to `null`.
 
     #### Limits
@@ -512,9 +513,10 @@
     When the default type is used, the maximal integer number that can be
     stored is `9223372036854775807` (INT64_MAX) and the minimal integer number
     that can be stored is `-9223372036854775808` (INT64_MIN). Integer numbers
-    that are out of range will yield over/underflow when used in a constructor.
-    During deserialization, too large or small integer numbers will be
-    automatically be stored as @ref number_unsigned_t or @ref number_float_t.
+    that are out of range will yield over/underflow when used in a
+    constructor. During deserialization, too large or small integer numbers
+    will be automatically be stored as @ref number_unsigned_t or @ref
+    number_float_t.
 
     [RFC 7159](http://rfc7159.net/rfc7159) further states:
     > Note that when such software is used, numbers that are integers and are
@@ -540,27 +542,27 @@
     @brief a type for a number (unsigned)
 
     [RFC 7159](http://rfc7159.net/rfc7159) describes numbers as follows:
-    > The representation of numbers is similar to that used in most programming
-    > languages. A number is represented in base 10 using decimal digits. It
-    > contains an integer component that may be prefixed with an optional minus
-    > sign, which may be followed by a fraction part and/or an exponent part.
-    > Leading zeros are not allowed. (...) Numeric values that cannot be
-    > represented in the grammar below (such as Infinity and NaN) are not
-    > permitted.
+    > The representation of numbers is similar to that used in most
+    > programming languages. A number is represented in base 10 using decimal
+    > digits. It contains an integer component that may be prefixed with an
+    > optional minus sign, which may be followed by a fraction part and/or an
+    > exponent part. Leading zeros are not allowed. (...) Numeric values that
+    > cannot be represented in the grammar below (such as Infinity and NaN)
+    > are not permitted.
 
-    This description includes both integer and floating-point numbers. However,
-    C++ allows more precise storage if it is known whether the number is a
-    signed integer, an unsigned integer or a floating-point number. Therefore,
-    three different types, @ref number_integer_t, @ref number_unsigned_t and
-    @ref number_float_t are used.
+    This description includes both integer and floating-point numbers.
+    However, C++ allows more precise storage if it is known whether the number
+    is a signed integer, an unsigned integer or a floating-point number.
+    Therefore, three different types, @ref number_integer_t, @ref
+    number_unsigned_t and @ref number_float_t are used.
 
-    To store unsigned integer numbers in C++, a type is defined by the template
-    parameter @a NumberUnsignedType which chooses the type to use.
+    To store unsigned integer numbers in C++, a type is defined by the
+    template parameter @a NumberUnsignedType which chooses the type to use.
 
     #### Default type
 
-    With the default values for @a NumberUnsignedType (`uint64_t`), the default
-    value for @a number_unsigned_t is:
+    With the default values for @a NumberUnsignedType (`uint64_t`), the
+    default value for @a number_unsigned_t is:
 
     @code {.cpp}
     uint64_t
@@ -571,8 +573,8 @@
     - The restrictions about leading zeros is not enforced in C++. Instead,
       leading zeros in integer literals lead to an interpretation as octal
       number. Internally, the value will be stored as decimal number. For
-      instance, the C++ integer literal `010` will be serialized to `8`. During
-      deserialization, leading zeros yield an error.
+      instance, the C++ integer literal `010` will be serialized to `8`.
+      During deserialization, leading zeros yield an error.
     - Not-a-number (NaN) values will be serialized to `null`.
 
     #### Limits
@@ -612,19 +614,19 @@
     @brief a type for a number (floating-point)
 
     [RFC 7159](http://rfc7159.net/rfc7159) describes numbers as follows:
-    > The representation of numbers is similar to that used in most programming
-    > languages. A number is represented in base 10 using decimal digits. It
-    > contains an integer component that may be prefixed with an optional minus
-    > sign, which may be followed by a fraction part and/or an exponent part.
-    > Leading zeros are not allowed. (...) Numeric values that cannot be
-    > represented in the grammar below (such as Infinity and NaN) are not
-    > permitted.
+    > The representation of numbers is similar to that used in most
+    > programming languages. A number is represented in base 10 using decimal
+    > digits. It contains an integer component that may be prefixed with an
+    > optional minus sign, which may be followed by a fraction part and/or an
+    > exponent part. Leading zeros are not allowed. (...) Numeric values that
+    > cannot be represented in the grammar below (such as Infinity and NaN)
+    > are not permitted.
 
-    This description includes both integer and floating-point numbers. However,
-    C++ allows more precise storage if it is known whether the number is a
-    signed integer, an unsigned integer or a floating-point number. Therefore,
-    three different types, @ref number_integer_t, @ref number_unsigned_t and
-    @ref number_float_t are used.
+    This description includes both integer and floating-point numbers.
+    However, C++ allows more precise storage if it is known whether the number
+    is a signed integer, an unsigned integer or a floating-point number.
+    Therefore, three different types, @ref number_integer_t, @ref
+    number_unsigned_t and @ref number_float_t are used.
 
     To store floating-point numbers in C++, a type is defined by the template
     parameter @a NumberFloatType which chooses the type to use.
@@ -641,8 +643,8 @@
     #### Default behavior
 
     - The restrictions about leading zeros is not enforced in C++. Instead,
-      leading zeros in floating-point literals will be ignored. Internally, the
-      value will be stored as decimal number. For instance, the C++
+      leading zeros in floating-point literals will be ignored. Internally,
+      the value will be stored as decimal number. For instance, the C++
       floating-point literal `01.2` will be serialized to `1.2`. During
       deserialization, leading zeros yield an error.
     - Not-a-number (NaN) values will be serialized to `null`.
@@ -653,9 +655,9 @@
     > This specification allows implementations to set limits on the range and
     > precision of numbers accepted. Since software that implements IEEE
     > 754-2008 binary64 (double precision) numbers is generally available and
-    > widely used, good interoperability can be achieved by implementations that
-    > expect no more precision or range than these provide, in the sense that
-    > implementations will approximate JSON numbers within the expected
+    > widely used, good interoperability can be achieved by implementations
+    > that expect no more precision or range than these provide, in the sense
+    > that implementations will approximate JSON numbers within the expected
     > precision.
 
     This implementation does exactly follow this approach, as it uses double
@@ -709,73 +711,6 @@
 
   private:
 
-    /*!
-    @brief a type to hold JSON type information
-
-    This bitfield type holds information about JSON types. It is internally
-    used to hold the basic JSON type enumeration, as well as additional
-    information in the case of values that have been parsed from a string
-    including whether of not it was created directly or parsed, and in the
-    case of floating point numbers the number of significant figures in the
-    original representaiton and if it was in exponential form, if a '+' was
-    included in the exponent and the capitilization of the exponent marker.
-    The sole purpose of this information is to permit accurate round trips.
-
-    @since version 2.0.0
-    */
-    union type_data_t
-    {
-        struct
-        {
-            /// the type of the value (@ref value_t)
-            uint16_t type : 4;
-            /// whether the number was parsed from a string
-            uint16_t parsed : 1;
-            /// whether parsed number contained an exponent ('e'/'E')
-            uint16_t has_exp : 1;
-            /// whether parsed number contained a plus in the exponent
-            uint16_t exp_plus : 1;
-            /// whether parsed number's exponent was capitalized ('E')
-            uint16_t exp_cap : 1;
-            /// the number of figures for a parsed number
-            uint16_t precision : 8;
-        } bits;
-        uint16_t data;
-
-        /// return the type as value_t
-        operator value_t() const
-        {
-            return static_cast<value_t>(bits.type);
-        }
-
-        /// test type for equality (ignore other fields)
-        bool operator==(const value_t& rhs) const
-        {
-            return static_cast<value_t>(bits.type) == rhs;
-        }
-
-        /// assignment
-        type_data_t& operator=(value_t rhs)
-        {
-            bits.type = static_cast<uint16_t>(rhs);
-            return *this;
-        }
-
-        /// construct from value_t
-        type_data_t(value_t t) noexcept
-        {
-            *reinterpret_cast<uint16_t*>(this) = 0;
-            bits.type = static_cast<uint16_t>(t);
-        }
-
-        /// default constructor
-        type_data_t() noexcept
-        {
-            data = 0;
-            bits.type = reinterpret_cast<uint16_t>(value_t::null);
-        }
-    };
-
     /// helper for exception-safe object creation
     template<typename T, typename... Args>
     static T* create(Args&& ... args)
@@ -938,9 +873,9 @@
     influenced. When passed to @ref parse(std::istream&, parser_callback_t) or
     @ref parse(const string_t&, parser_callback_t), it is called on certain
     events (passed as @ref parse_event_t via parameter @a event) with a set
-    recursion depth @a depth and context JSON value @a parsed. The return value
-    of the callback function is a boolean indicating whether the element that
-    emitted the callback shall be kept or not.
+    recursion depth @a depth and context JSON value @a parsed. The return
+    value of the callback function is a boolean indicating whether the element
+    that emitted the callback shall be kept or not.
 
     We distinguish six scenarios (determined by the event type) in which the
     callback function can be called. The following table describes the values
@@ -960,8 +895,8 @@
 
     - Discarded values in structured types are skipped. That is, the parser
       will behave as if the discarded value was never read.
-    - In case a value outside a structured type is skipped, it is replaced with
-      `null`. This case happens if the top-level element is skipped.
+    - In case a value outside a structured type is skipped, it is replaced
+      with `null`. This case happens if the top-level element is skipped.
 
     @param[in] depth  the depth of the recursion during parsing
 
@@ -1065,8 +1000,8 @@
     Create a `null` JSON value. This is the explicitly version of the `null`
     value constructor as it takes a null pointer as parameter. It allows to
     create `null` values by explicitly assigning a `nullptr` to a JSON value.
-    The passed null pointer itself is not read -- it is only used to choose the
-    right constructor.
+    The passed null pointer itself is not read -- it is only used to choose
+    the right constructor.
 
     @complexity Constant.
 
@@ -1096,8 +1031,8 @@
 
     @throw std::bad_alloc if allocation for object value fails
 
-    @liveexample{The following code shows the constructor with an @ref object_t
-    parameter.,basic_json__object_t}
+    @liveexample{The following code shows the constructor with an @ref
+    object_t parameter.,basic_json__object_t}
 
     @sa @ref basic_json(const CompatibleObjectType&) -- create an object value
     from a compatible STL container
@@ -1177,11 +1112,11 @@
     any type @a CompatibleArrayType that can be used to construct values of
     type @ref array_t.
 
-    @tparam CompatibleArrayType An object type whose `value_type` is compatible
-    to @ref array_t. Examples include `std::vector`, `std::deque`, `std::list`,
-    `std::forward_list`, `std::array`, `std::set`, `std::unordered_set`,
-    `std::multiset`, and `unordered_multiset` with a `value_type` from which a
-    @ref basic_json value can be constructed.
+    @tparam CompatibleArrayType An object type whose `value_type` is
+    compatible to @ref array_t. Examples include `std::vector`, `std::deque`,
+    `std::list`, `std::forward_list`, `std::array`, `std::set`,
+    `std::unordered_set`, `std::multiset`, and `unordered_multiset` with a
+    `value_type` from which a @ref basic_json value can be constructed.
 
     @param[in] val  a value for the array
 
@@ -1225,8 +1160,8 @@
 
     @throw std::bad_alloc if allocation for string value fails
 
-    @liveexample{The following code shows the constructor with an @ref string_t
-    parameter.,basic_json__string_t}
+    @liveexample{The following code shows the constructor with an @ref
+    string_t parameter.,basic_json__string_t}
 
     @sa @ref basic_json(const typename string_t::value_type*) -- create a
     string value from a character pointer
@@ -1318,9 +1253,9 @@
     Create an integer number JSON value with a given content.
 
     @tparam T A helper type to remove this function via SFINAE in case @ref
-    number_integer_t is the same as `int`. In this case, this constructor would
-    have the same signature as @ref basic_json(const int value). Note the
-    helper type @a T is not visible in this constructor's interface.
+    number_integer_t is the same as `int`. In this case, this constructor
+    would have the same signature as @ref basic_json(const int value). Note
+    the helper type @a T is not visible in this constructor's interface.
 
     @param[in] val  an integer to create a JSON number from
 
@@ -1355,8 +1290,8 @@
     @note This constructor allows to pass enums directly to a constructor. As
     C++ has no way of specifying the type of an anonymous enum explicitly, we
     can only rely on the fact that such values implicitly convert to int. As
-    int may already be the same type of number_integer_t, we may need to switch
-    off the constructor @ref basic_json(const number_integer_t).
+    int may already be the same type of number_integer_t, we may need to
+    switch off the constructor @ref basic_json(const number_integer_t).
 
     @complexity Constant.
 
@@ -1383,8 +1318,8 @@
     construct values of type @ref number_integer_t.
 
     @tparam CompatibleNumberIntegerType An integer type which is compatible to
-    @ref number_integer_t. Examples include the types `int`, `int32_t`, `long`,
-    and `short`.
+    @ref number_integer_t. Examples include the types `int`, `int32_t`,
+    `long`, and `short`.
 
     @param[in] val  an integer to create a JSON number from
 
@@ -1442,12 +1377,12 @@
     /*!
     @brief create an unsigned number (implicit)
 
-    Create an unsigned number JSON value with a given content. This constructor
-    allows any type @a CompatibleNumberUnsignedType that can be used to
-    construct values of type @ref number_unsigned_t.
+    Create an unsigned number JSON value with a given content. This
+    constructor allows any type @a CompatibleNumberUnsignedType that can be
+    used to construct values of type @ref number_unsigned_t.
 
-    @tparam CompatibleNumberUnsignedType An integer type which is compatible to
-    @ref number_unsigned_t. Examples may include the types `unsigned int`,
+    @tparam CompatibleNumberUnsignedType An integer type which is compatible
+    to @ref number_unsigned_t. Examples may include the types `unsigned int`,
     `uint32_t`, or `unsigned short`.
 
     @param[in] val  an unsigned integer to create a JSON number from
@@ -1459,13 +1394,13 @@
 
     @since version 2.0.0
     */
-    template < typename CompatibleNumberUnsignedType, typename
-               std::enable_if <
-                   std::is_constructible<number_unsigned_t, CompatibleNumberUnsignedType>::value and
-                   std::numeric_limits<CompatibleNumberUnsignedType>::is_integer and
-                   !std::numeric_limits<CompatibleNumberUnsignedType>::is_signed,
-                   CompatibleNumberUnsignedType >::type
-               = 0 >
+    template <typename CompatibleNumberUnsignedType, typename
+              std::enable_if <
+                  std::is_constructible<number_unsigned_t, CompatibleNumberUnsignedType>::value and
+                  std::numeric_limits<CompatibleNumberUnsignedType>::is_integer and
+                  not std::numeric_limits<CompatibleNumberUnsignedType>::is_signed,
+                  CompatibleNumberUnsignedType>::type
+              = 0>
     basic_json(const CompatibleNumberUnsignedType val) noexcept
         : m_type(value_t::number_unsigned),
           m_value(static_cast<number_unsigned_t>(val))
@@ -1480,8 +1415,8 @@
 
     @note [RFC 7159](http://www.rfc-editor.org/rfc/rfc7159.txt), section 6
     disallows NaN values:
-    > Numeric values that cannot be represented in the grammar below (such
-    > as Infinity and NaN) are not permitted.
+    > Numeric values that cannot be represented in the grammar below (such as
+    > Infinity and NaN) are not permitted.
     In case the parameter @a val is not a number, a JSON null value is
     created instead.
 
@@ -1513,15 +1448,16 @@
     constructor allows any type @a CompatibleNumberFloatType that can be used
     to construct values of type @ref number_float_t.
 
-    @tparam CompatibleNumberFloatType A floating-point type which is compatible
-    to @ref number_float_t. Examples may include the types `float` or `double`.
+    @tparam CompatibleNumberFloatType A floating-point type which is
+    compatible to @ref number_float_t. Examples may include the types `float`
+    or `double`.
 
     @param[in] val  a floating-point to create a JSON number from
 
     @note [RFC 7159](http://www.rfc-editor.org/rfc/rfc7159.txt), section 6
     disallows NaN values:
-    > Numeric values that cannot be represented in the grammar below (such
-    > as Infinity and NaN) are not permitted.
+    > Numeric values that cannot be represented in the grammar below (such as
+    > Infinity and NaN) are not permitted.
     In case the parameter @a val is not a number, a JSON null value is
     created instead.
 
@@ -1592,13 +1528,13 @@
     used by the functions @ref array(std::initializer_list<basic_json>) and
     @ref object(std::initializer_list<basic_json>).
 
-    @param[in] manual_type internal parameter; when @a type_deduction is set to
-    `false`, the created JSON value will use the provided type (only @ref
+    @param[in] manual_type internal parameter; when @a type_deduction is set
+    to `false`, the created JSON value will use the provided type (only @ref
     value_t::array and @ref value_t::object are valid); when @a type_deduction
     is set to `true`, this parameter has no effect
 
-    @throw std::domain_error if @a type_deduction is `false`, @a manual_type is
-    `value_t::object`, but @a init contains an element which is not a pair
+    @throw std::domain_error if @a type_deduction is `false`, @a manual_type
+    is `value_t::object`, but @a init contains an element which is not a pair
     whose first element is a string; example: `"cannot create object from
     initializer list"`
 
@@ -1679,8 +1615,8 @@
     list of values `a, b, c`, creates the JSON value `[a, b, c]`. If the
     initializer list is empty, the empty array `[]` is created.
 
-    @note This function is only needed to express two edge cases that cannot be
-    realized with the initializer list constructor (@ref
+    @note This function is only needed to express two edge cases that cannot
+    be realized with the initializer list constructor (@ref
     basic_json(std::initializer_list<basic_json>, bool, value_t)). These cases
     are:
     1. creating an array whose elements are all pairs whose first element is a
@@ -1723,8 +1659,8 @@
     related function @ref array(std::initializer_list<basic_json>), there are
     no cases which can only be expressed by this function. That is, any
     initializer list @a init can also be passed to the initializer list
-    constructor
-    @ref basic_json(std::initializer_list<basic_json>, bool, value_t).
+    constructor @ref basic_json(std::initializer_list<basic_json>, bool,
+    value_t).
 
     @param[in] init  initializer list to create an object from (optional)
 
@@ -1755,8 +1691,8 @@
     /*!
     @brief construct an array with count copies of given value
 
-    Constructs a JSON array value by creating @a cnt copies of a passed
-    value. In case @a cnt is `0`, an empty array is created. As postcondition,
+    Constructs a JSON array value by creating @a cnt copies of a passed value.
+    In case @a cnt is `0`, an empty array is created. As postcondition,
     `std::distance(begin(),end()) == cnt` holds.
 
     @param[in] cnt  the number of JSON copies of @a val to create
@@ -1784,8 +1720,8 @@
     - In case of primitive types (number, boolean, or string), @a first must
       be `begin()` and @a last must be `end()`. In this case, the value is
       copied. Otherwise, std::out_of_range is thrown.
-    - In case of structured types (array, object), the constructor behaves
-      as similar versions for `std::vector`.
+    - In case of structured types (array, object), the constructor behaves as
+      similar versions for `std::vector`.
     - In case of a null type, std::domain_error is thrown.
 
     @tparam InputIT an input iterator type (@ref iterator or @ref
@@ -1800,8 +1736,8 @@
     boolean, or string) where an out of range error can be detected easily;
     example: `"iterators out of range"`
     @throw std::bad_alloc if allocation for object, array, or string fails
-    @throw std::domain_error if called with a null value; example: `"cannot use
-    construct with iterators from null"`
+    @throw std::domain_error if called with a null value; example: `"cannot
+    use construct with iterators from null"`
 
     @complexity Linear in distance between @a first and @a last.
 
@@ -2042,8 +1978,8 @@
     @brief copy assignment
 
     Copy assignment operator. Copies a JSON value via the "copy and swap"
-    strategy: It is expressed in terms of the copy constructor, destructor, and
-    the swap() member function.
+    strategy: It is expressed in terms of the copy constructor, destructor,
+    and the swap() member function.
 
     @param[in] other  value to copy from
 
@@ -2143,8 +2079,8 @@
     parameter.
 
     @param[in] indent if indent is nonnegative, then array elements and object
-    members will be pretty-printed with that indent level. An indent level of 0
-    will only insert newlines. -1 (the default) selects the most compact
+    members will be pretty-printed with that indent level. An indent level of
+    0 will only insert newlines. -1 (the default) selects the most compact
     representation
 
     @return string containing the serialization of the JSON value
@@ -2976,8 +2912,8 @@
     /*!
     @brief get a reference value (implicit)
 
-    Implict reference access to the internally stored JSON value. No copies are
-    made.
+    Implict reference access to the internally stored JSON value. No copies
+    are made.
 
     @warning Writing data to the referee of the result yields an undefined
     state.
@@ -3027,14 +2963,14 @@
     /*!
     @brief get a value (implicit)
 
-    Implicit type conversion between the JSON value and a compatible value. The
-    call is realized by calling @ref get() const.
+    Implicit type conversion between the JSON value and a compatible value.
+    The call is realized by calling @ref get() const.
 
     @tparam ValueType non-pointer type compatible to the JSON value, for
     instance `int` for JSON integer numbers, `bool` for JSON booleans, or
-    `std::vector` types for JSON arrays. The character type of @ref string_t as
-    well as an initializer list of this type is excluded to avoid ambiguities
-    as these types implicitly convert to `std::string`.
+    `std::vector` types for JSON arrays. The character type of @ref string_t
+    as well as an initializer list of this type is excluded to avoid
+    ambiguities as these types implicitly convert to `std::string`.
 
     @return copy of the JSON value, converted to type @a ValueType
 
@@ -3123,8 +3059,8 @@
     /*!
     @brief access specified array element with bounds checking
 
-    Returns a const reference to the element at specified location @a idx, with
-    bounds checking.
+    Returns a const reference to the element at specified location @a idx,
+    with bounds checking.
 
     @param[in] idx  index of the element to access
 
@@ -3215,8 +3151,8 @@
     /*!
     @brief access specified object element with bounds checking
 
-    Returns a const reference to the element at with specified key @a key, with
-    bounds checking.
+    Returns a const reference to the element at with specified key @a key,
+    with bounds checking.
 
     @param[in] key  key of the element to access
 
@@ -3273,8 +3209,8 @@
 
     @return reference to the element at index @a idx
 
-    @throw std::domain_error if JSON is not an array or null; example: `"cannot
-    use operator[] with string"`
+    @throw std::domain_error if JSON is not an array or null; example:
+    `"cannot use operator[] with string"`
 
     @complexity Constant if @a idx is in the range of the array. Otherwise
     linear in `idx - size()`.
@@ -3690,8 +3626,8 @@
 
     @complexity Constant.
 
-    @pre The JSON value must not be `null` (would throw `std::out_of_range`) or
-    an empty array or object (undefined behavior, guarded by assertions).
+    @pre The JSON value must not be `null` (would throw `std::out_of_range`)
+    or an empty array or object (undefined behavior, guarded by assertions).
     @post The JSON value remains unchanged.
 
     @throw std::out_of_range when called on `null` value
@@ -3732,8 +3668,8 @@
 
     @complexity Constant.
 
-    @pre The JSON value must not be `null` (would throw `std::out_of_range`) or
-    an empty array or object (undefined behavior, guarded by assertions).
+    @pre The JSON value must not be `null` (would throw `std::out_of_range`)
+    or an empty array or object (undefined behavior, guarded by assertions).
     @post The JSON value remains unchanged.
 
     @throw std::out_of_range when called on `null` value.
@@ -3772,16 +3708,16 @@
     will be `null`.
 
     @param[in] pos iterator to the element to remove
-    @return Iterator following the last removed element. If the iterator @a pos
-    refers to the last element, the `end()` iterator is returned.
+    @return Iterator following the last removed element. If the iterator @a
+    pos refers to the last element, the `end()` iterator is returned.
 
     @tparam InteratorType an @ref iterator or @ref const_iterator
 
     @post Invalidates iterators and references at or after the point of the
     erase, including the `end()` iterator.
 
-    @throw std::domain_error if called on a `null` value; example: `"cannot use
-    erase() with null"`
+    @throw std::domain_error if called on a `null` value; example: `"cannot
+    use erase() with null"`
     @throw std::domain_error if called on an iterator which does not belong to
     the current JSON value; example: `"iterator does not fit current value"`
     @throw std::out_of_range if called on a primitive type with invalid
@@ -3797,12 +3733,12 @@
     @liveexample{The example shows the result of `erase()` for different JSON
     types.,erase__IteratorType}
 
-    @sa @ref erase(InteratorType, InteratorType) -- removes the elements in the
-    given range
+    @sa @ref erase(InteratorType, InteratorType) -- removes the elements in
+    the given range
     @sa @ref erase(const typename object_t::key_type&) -- removes the element
     from an object at the given key
-    @sa @ref erase(const size_type) -- removes the element from an array at the
-    given index
+    @sa @ref erase(const size_type) -- removes the element from an array at
+    the given index
 
     @since version 1.0.0
     */
@@ -3871,9 +3807,9 @@
     /*!
     @brief remove elements given an iterator range
 
-    Removes the element specified by the range `[first; last)`. The iterator @a
-    first does not need to be dereferenceable if `first == last`: erasing an
-    empty range is a no-op.
+    Removes the element specified by the range `[first; last)`. The iterator
+    @a first does not need to be dereferenceable if `first == last`: erasing
+    an empty range is a no-op.
 
     If called on a primitive type other than `null`, the resulting JSON value
     will be `null`.
@@ -3888,8 +3824,8 @@
     @post Invalidates iterators and references at or after the point of the
     erase, including the `end()` iterator.
 
-    @throw std::domain_error if called on a `null` value; example: `"cannot use
-    erase() with null"`
+    @throw std::domain_error if called on a `null` value; example: `"cannot
+    use erase() with null"`
     @throw std::domain_error if called on iterators which does not belong to
     the current JSON value; example: `"iterators do not fit current value"`
     @throw std::out_of_range if called on a primitive type with invalid
@@ -3909,8 +3845,8 @@
     @sa @ref erase(InteratorType) -- removes the element at a given position
     @sa @ref erase(const typename object_t::key_type&) -- removes the element
     from an object at the given key
-    @sa @ref erase(const size_type) -- removes the element from an array at the
-    given index
+    @sa @ref erase(const size_type) -- removes the element from an array at
+    the given index
 
     @since version 1.0.0
     */
@@ -3986,8 +3922,8 @@
     @param[in] key value of the elements to remove
 
     @return Number of elements removed. If @a ObjectType is the default
-    `std::map` type, the return value will always be `0` (@a key was not found)
-    or `1` (@a key was found).
+    `std::map` type, the return value will always be `0` (@a key was not
+    found) or `1` (@a key was found).
 
     @post References and iterators to the erased elements are invalidated.
     Other references and iterators are not affected.
@@ -4000,10 +3936,10 @@
     @liveexample{The example shows the effect of `erase()`.,erase__key_type}
 
     @sa @ref erase(InteratorType) -- removes the element at a given position
-    @sa @ref erase(InteratorType, InteratorType) -- removes the elements in the
-    given range
-    @sa @ref erase(const size_type) -- removes the element from an array at the
-    given index
+    @sa @ref erase(InteratorType, InteratorType) -- removes the elements in
+    the given range
+    @sa @ref erase(const size_type) -- removes the element from an array at
+    the given index
 
     @since version 1.0.0
     */
@@ -4030,16 +3966,16 @@
 
     @throw std::domain_error when called on a type other than JSON array;
     example: `"cannot use erase() with null"`
-    @throw std::out_of_range when `idx >= size()`; example: `"index out of
-    range"`
+    @throw std::out_of_range when `idx >= size()`; example: `"array index 17
+    is out of range"`
 
     @complexity Linear in distance between @a idx and the end of the container.
 
     @liveexample{The example shows the effect of `erase()`.,erase__size_type}
 
     @sa @ref erase(InteratorType) -- removes the element at a given position
-    @sa @ref erase(InteratorType, InteratorType) -- removes the elements in the
-    given range
+    @sa @ref erase(InteratorType, InteratorType) -- removes the elements in
+    the given range
     @sa @ref erase(const typename object_t::key_type&) -- removes the element
     from an object at the given key
 
@@ -4052,7 +3988,7 @@
         {
             if (idx >= size())
             {
-                throw std::out_of_range("index out of range");
+                throw std::out_of_range("array index " + std::to_string(idx) + " is out of range");
             }
 
             assert(m_value.array != nullptr);
@@ -4078,7 +4014,8 @@
     @brief find an element in a JSON object
 
     Finds an element in a JSON object with key equivalent to @a key. If the
-    element is not found or the JSON value is not an object, end() is returned.
+    element is not found or the JSON value is not an object, end() is
+    returned.
 
     @param[in] key key value of the element to search for
 
@@ -4438,9 +4375,9 @@
     @brief wrapper to access iterator member functions in range-based for
 
     This function allows to access @ref iterator::key() and @ref
-    iterator::value() during range-based for loops. In these loops, a reference
-    to the JSON values is returned, so there is no access to the underlying
-    iterator.
+    iterator::value() during range-based for loops. In these loops, a
+    reference to the JSON values is returned, so there is no access to the
+    underlying iterator.
 
     @note The name of this function is not yet final and may change in the
     future.
@@ -4484,8 +4421,8 @@
             object      | result of function `object_t::empty()`
             array       | result of function `array_t::empty()`
 
-    @complexity Constant, as long as @ref array_t and @ref object_t satisfy the
-    Container concept; that is, their `empty()` functions have constant
+    @complexity Constant, as long as @ref array_t and @ref object_t satisfy
+    the Container concept; that is, their `empty()` functions have constant
     complexity.
 
     @requirement This function helps `basic_json` satisfying the
@@ -4547,8 +4484,9 @@
             object      | result of function object_t::size()
             array       | result of function array_t::size()
 
-    @complexity Constant, as long as @ref array_t and @ref object_t satisfy the
-    Container concept; that is, their size() functions have constant complexity.
+    @complexity Constant, as long as @ref array_t and @ref object_t satisfy
+    the Container concept; that is, their size() functions have constant
+    complexity.
 
     @requirement This function helps `basic_json` satisfying the
     [Container](http://en.cppreference.com/w/cpp/concept/Container)
@@ -4612,8 +4550,8 @@
             object      | result of function `object_t::max_size()`
             array       | result of function `array_t::max_size()`
 
-    @complexity Constant, as long as @ref array_t and @ref object_t satisfy the
-    Container concept; that is, their `max_size()` functions have constant
+    @complexity Constant, as long as @ref array_t and @ref object_t satisfy
+    the Container concept; that is, their `max_size()` functions have constant
     complexity.
 
     @requirement This function helps `basic_json` satisfying the
@@ -4835,8 +4773,8 @@
     @brief add an object to an object
 
     Inserts the given element @a val to the JSON object. If the function is
-    called on a JSON null value, an empty object is created before inserting @a
-    val.
+    called on a JSON null value, an empty object is created before inserting
+    @a val.
 
     @param[in] val the value to add to the JSON object
 
@@ -4878,7 +4816,55 @@
     reference operator+=(const typename object_t::value_type& val)
     {
         push_back(val);
-        return operator[](val.first);
+        return *this;
+    }
+
+    /*!
+    @brief add an object to an object
+
+    This function allows to use `push_back` with an initializer list. In case
+
+    1. the current value is an object,
+    2. the initializer list @a init contains only two elements, and
+    3. the first element of @a init is a string,
+
+    @a init is converted into an object element and added using
+    @ref push_back(const typename object_t::value_type&). Otherwise, @a init
+    is converted to a JSON value and added using @ref push_back(basic_json&&).
+
+    @param init  an initializer list
+
+    @complexity Linear in the size of the initializer list @a init.
+
+    @note This function is required to resolve an ambiguous overload error,
+          because pairs like `{"key", "value"}` can be both interpreted as
+          `object_t::value_type` or `std::initializer_list<basic_json>`, see
+          https://github.com/nlohmann/json/issues/235 for more information.
+
+    @liveexample{The example shows how initializer lists are treated as
+    objects when possible.,push_back__initializer_list}
+    */
+    void push_back(std::initializer_list<basic_json> init)
+    {
+        if (is_object() and init.size() == 2 and init.begin()->is_string())
+        {
+            const string_t key = *init.begin();
+            push_back(typename object_t::value_type(key, *(init.begin() + 1)));
+        }
+        else
+        {
+            push_back(basic_json(init));
+        }
+    }
+
+    /*!
+    @brief add an object to an object
+    @copydoc push_back(std::initializer_list<basic_json>)
+    */
+    reference operator+=(std::initializer_list<basic_json> init)
+    {
+        push_back(init);
+        return *this;
     }
 
     /*!
@@ -5026,6 +5012,7 @@
             throw std::domain_error("iterator does not fit current value");
         }
 
+        // check if range iterators belong to the same JSON object
         if (first.m_object != last.m_object)
         {
             throw std::domain_error("iterators do not fit");
@@ -5063,8 +5050,8 @@
     @return iterator pointing to the first element inserted, or @a pos if
     `ilist` is empty
 
-    @complexity Linear in `ilist.size()` plus linear in the distance between @a
-    pos and end of the container.
+    @complexity Linear in `ilist.size()` plus linear in the distance between
+    @a pos and end of the container.
 
     @liveexample{The example shows how `insert()` is used.,insert__ilist}
 
@@ -5709,8 +5696,8 @@
 
     @note A UTF-8 byte order mark is silently ignored.
 
-    @liveexample{The example below demonstrates the `parse()` function with and
-    without callback function.,parse__string__parser_callback_t}
+    @liveexample{The example below demonstrates the `parse()` function with
+    and without callback function.,parse__string__parser_callback_t}
 
     @sa @ref parse(std::istream&, parser_callback_t) for a version that reads
     from an input stream
@@ -5738,11 +5725,11 @@
 
     @note A UTF-8 byte order mark is silently ignored.
 
-    @liveexample{The example below demonstrates the `parse()` function with and
-    without callback function.,parse__istream__parser_callback_t}
+    @liveexample{The example below demonstrates the `parse()` function with
+    and without callback function.,parse__istream__parser_callback_t}
 
-    @sa @ref parse(const string_t&, parser_callback_t) for a version that reads
-    from a string
+    @sa @ref parse(const string_t&, parser_callback_t) for a version that
+    reads from a string
 
     @since version 1.0.0
     */
@@ -5875,8 +5862,8 @@
     /*!
     @brief escape a string
 
-    Escape a string by replacing certain special characters by a sequence of an
-    escape character (backslash) and another character and other control
+    Escape a string by replacing certain special characters by a sequence of
+    an escape character (backslash) and another character and other control
     characters by a sequence of "\u" followed by a four-digit hex
     representation.
 
@@ -5963,9 +5950,11 @@
                     {
                         // convert a number 0..15 to its hex representation
                         // (0..f)
-                        auto hexify = [](const char v) -> char
+                        const auto hexify = [](const int v) -> char
                         {
-                            return (v < 10) ? ('0' + v) : ('a' + v - 10);
+                            return (v < 10)
+                            ? ('0' + static_cast<char>(v))
+                            : ('a' + static_cast<char>((v - 10) & 0x1f));
                         };
 
                         // print character c as \uxxxx
@@ -5996,8 +5985,8 @@
 
     This function is called by the public member function dump and organizes
     the serialization internally. The indentation level is propagated as
-    additional parameter. In case of arrays and objects, the function is called
-    recursively. Note that
+    additional parameter. In case of arrays and objects, the function is
+    called recursively. Note that
 
     - strings and object keys are escaped using `escape_string()`
     - integer numbers are converted implicitly via `operator<<`
@@ -6127,79 +6116,23 @@
 
             case value_t::number_float:
             {
-                // check if number was parsed from a string
-                if (m_type.bits.parsed)
+                if (m_value.number_float == 0)
                 {
-                    // check if parsed number had an exponent given
-                    if (m_type.bits.has_exp)
-                    {
-                        // buffer size: precision (2^8-1 = 255) + other ('-.e-xxx' = 7) + null (1)
-                        char buf[263];
-                        int len;
-
-                        // handle capitalization of the exponent
-                        if (m_type.bits.exp_cap)
-                        {
-                            len = snprintf(buf, sizeof(buf), "%.*E",
-                                           m_type.bits.precision, m_value.number_float) + 1;
-                        }
-                        else
-                        {
-                            len = snprintf(buf, sizeof(buf), "%.*e",
-                                           m_type.bits.precision, m_value.number_float) + 1;
-                        }
-
-                        // remove '+' sign from the exponent if necessary
-                        if (not m_type.bits.exp_plus)
-                        {
-                            if (len > static_cast<int>(sizeof(buf)))
-                            {
-                                len = sizeof(buf);
-                            }
-                            for (int i = 0; i < len; i++)
-                            {
-                                if (buf[i] == '+')
-                                {
-                                    for (; i + 1 < len; i++)
-                                    {
-                                        buf[i] = buf[i + 1];
-                                    }
-                                }
-                            }
-                        }
-
-                        o << buf;
-                    }
-                    else
-                    {
-                        // no exponent - output as a decimal
-                        std::stringstream ss;
-                        ss.imbue(std::locale(std::locale(), new DecimalSeparator));  // fix locale problems
-                        ss << std::setprecision(m_type.bits.precision)
-                           << std::fixed << m_value.number_float;
-                        o << ss.str();
-                    }
+                    // special case for zero to get "0.0"/"-0.0"
+                    o << (std::signbit(m_value.number_float) ? "-0.0" : "0.0");
                 }
                 else
                 {
-                    if (m_value.number_float == 0)
-                    {
-                        // special case for zero to get "0.0"/"-0.0"
-                        o << (std::signbit(m_value.number_float) ? "-0.0" : "0.0");
-                    }
-                    else
-                    {
-                        // Otherwise 6, 15 or 16 digits of precision allows
-                        // round-trip IEEE 754 string->float->string,
-                        // string->double->string or string->long double->string;
-                        // to be safe, we read this value from
-                        // std::numeric_limits<number_float_t>::digits10
-                        std::stringstream ss;
-                        ss.imbue(std::locale(std::locale(), new DecimalSeparator));  // fix locale problems
-                        ss << std::setprecision(std::numeric_limits<double>::digits10)
-                           << m_value.number_float;
-                        o << ss.str();
-                    }
+                    // Otherwise 6, 15 or 16 digits of precision allows
+                    // round-trip IEEE 754 string->float->string,
+                    // string->double->string or string->long
+                    // double->string; to be safe, we read this value from
+                    // std::numeric_limits<number_float_t>::digits10
+                    std::stringstream ss;
+                    ss.imbue(std::locale(std::locale(), new DecimalSeparator));  // fix locale problems
+                    ss << std::setprecision(std::numeric_limits<double>::digits10)
+                       << m_value.number_float;
+                    o << ss.str();
                 }
                 return;
             }
@@ -6224,7 +6157,7 @@
     //////////////////////
 
     /// the type of the current element
-    type_data_t m_type = value_t::null;
+    value_t m_type = value_t::null;
 
     /// the value of the current element
     json_value m_value = {};
@@ -7000,13 +6933,13 @@
         }
 
         /// return a reference to the value pointed to by the iterator
-        reference operator*()
+        reference operator*() const
         {
             return const_cast<reference>(base_iterator::operator*());
         }
 
         /// dereference the iterator
-        pointer operator->()
+        pointer operator->() const
         {
             return const_cast<pointer>(base_iterator::operator->());
         }
@@ -7212,8 +7145,8 @@
     @brief lexical analysis
 
     This class organizes the lexical analysis during JSON deserialization. The
-    core of it is a scanner generated by [re2c](http://re2c.org) that processes
-    a buffer and recognizes tokens according to RFC 7159.
+    core of it is a scanner generated by [re2c](http://re2c.org) that
+    processes a buffer and recognizes tokens according to RFC 7159.
     */
     class lexer
     {
@@ -7222,17 +7155,17 @@
         enum class token_type
         {
             uninitialized,   ///< indicating the scanner is uninitialized
-            literal_true,    ///< the "true" literal
-            literal_false,   ///< the "false" literal
-            literal_null,    ///< the "null" literal
+            literal_true,    ///< the `true` literal
+            literal_false,   ///< the `false` literal
+            literal_null,    ///< the `null` literal
             value_string,    ///< a string -- use get_string() for actual value
             value_number,    ///< a number -- use get_number() for actual value
-            begin_array,     ///< the character for array begin "["
-            begin_object,    ///< the character for object begin "{"
-            end_array,       ///< the character for array end "]"
-            end_object,      ///< the character for object end "}"
-            name_separator,  ///< the name separator ":"
-            value_separator, ///< the value separator ","
+            begin_array,     ///< the character for array begin `[`
+            begin_object,    ///< the character for object begin `{`
+            end_array,       ///< the character for array end `]`
+            end_object,      ///< the character for object end `}`
+            name_separator,  ///< the name separator `:`
+            value_separator, ///< the value separator `,`
             parse_error,     ///< indicating a parse error
             end_of_input     ///< indicating the end of the input buffer
         };
@@ -7277,7 +7210,7 @@
 
         @return string representation of the code point
 
-        @throw std::out_of_range if code point is >0x10ffff; example: `"code
+        @throw std::out_of_range if code point is > 0x10ffff; example: `"code
         points above 0x10FFFF are invalid"`
         @throw std::invalid_argument if the low surrogate is invalid; example:
         `""missing or wrong low surrogate""`
@@ -7416,322 +7349,323 @@
                 {
                     0,   0,   0,   0,   0,   0,   0,   0,
                     0,  32,  32,   0,   0,  32,   0,   0,
-                    64,  64,  64,  64,  64,  64,  64,  64,
-                    64,  64,  64,  64,  64,  64,  64,  64,
-                    96,  64,   0,  64,  64,  64,  64,  64,
-                    64,  64,  64,  64,  64,  64,  64,  64,
+                    128, 128, 128, 128, 128, 128, 128, 128,
+                    128, 128, 128, 128, 128, 128, 128, 128,
+                    160, 128,   0, 128, 128, 128, 128, 128,
+                    128, 128, 128, 128, 128, 128, 128, 128,
                     192, 192, 192, 192, 192, 192, 192, 192,
-                    192, 192,  64,  64,  64,  64,  64,  64,
-                    64,  64,  64,  64,  64,  64,  64,  64,
-                    64,  64,  64,  64,  64,  64,  64,  64,
-                    64,  64,  64,  64,  64,  64,  64,  64,
-                    64,  64,  64,  64,   0,  64,  64,  64,
-                    64,  64,  64,  64,  64,  64,  64,  64,
-                    64,  64,  64,  64,  64,  64,  64,  64,
-                    64,  64,  64,  64,  64,  64,  64,  64,
-                    64,  64,  64,  64,  64,  64,  64,  64,
-                    64,  64,  64,  64,  64,  64,  64,  64,
-                    64,  64,  64,  64,  64,  64,  64,  64,
-                    64,  64,  64,  64,  64,  64,  64,  64,
-                    64,  64,  64,  64,  64,  64,  64,  64,
-                    64,  64,  64,  64,  64,  64,  64,  64,
-                    64,  64,  64,  64,  64,  64,  64,  64,
-                    64,  64,  64,  64,  64,  64,  64,  64,
-                    64,  64,  64,  64,  64,  64,  64,  64,
-                    64,  64,  64,  64,  64,  64,  64,  64,
-                    64,  64,  64,  64,  64,  64,  64,  64,
-                    64,  64,  64,  64,  64,  64,  64,  64,
-                    64,  64,  64,  64,  64,  64,  64,  64,
-                    64,  64,  64,  64,  64,  64,  64,  64,
-                    64,  64,  64,  64,  64,  64,  64,  64,
-                    64,  64,  64,  64,  64,  64,  64,  64,
-                    64,  64,  64,  64,  64,  64,  64,  64,
+                    192, 192, 128, 128, 128, 128, 128, 128,
+                    128, 128, 128, 128, 128, 128, 128, 128,
+                    128, 128, 128, 128, 128, 128, 128, 128,
+                    128, 128, 128, 128, 128, 128, 128, 128,
+                    128, 128, 128, 128,   0, 128, 128, 128,
+                    128, 128, 128, 128, 128, 128, 128, 128,
+                    128, 128, 128, 128, 128, 128, 128, 128,
+                    128, 128, 128, 128, 128, 128, 128, 128,
+                    128, 128, 128, 128, 128, 128, 128, 128,
+                    128, 128, 128, 128, 128, 128, 128, 128,
+                    128, 128, 128, 128, 128, 128, 128, 128,
+                    128, 128, 128, 128, 128, 128, 128, 128,
+                    128, 128, 128, 128, 128, 128, 128, 128,
+                    128, 128, 128, 128, 128, 128, 128, 128,
+                    128, 128, 128, 128, 128, 128, 128, 128,
+                    128, 128, 128, 128, 128, 128, 128, 128,
+                    128, 128, 128, 128, 128, 128, 128, 128,
+                    128, 128, 128, 128, 128, 128, 128, 128,
+                    128, 128, 128, 128, 128, 128, 128, 128,
+                    128, 128, 128, 128, 128, 128, 128, 128,
+                    128, 128, 128, 128, 128, 128, 128, 128,
+                    128, 128, 128, 128, 128, 128, 128, 128,
+                    128, 128, 128, 128, 128, 128, 128, 128,
+                    128, 128, 128, 128, 128, 128, 128, 128,
+                    128, 128, 128, 128, 128, 128, 128, 128,
                 };
                 if ((m_limit - m_cursor) < 5)
                 {
                     yyfill();    // LCOV_EXCL_LINE;
                 }
                 yych = *m_cursor;
-                if (yych <= ':')
+                if (yybm[0 + yych] & 32)
                 {
-                    if (yych <= ' ')
+                    goto basic_json_parser_6;
+                }
+                if (yych <= '\\')
+                {
+                    if (yych <= '-')
                     {
-                        if (yych <= '\n')
+                        if (yych <= '"')
                         {
                             if (yych <= 0x00)
                             {
-                                goto basic_json_parser_28;
+                                goto basic_json_parser_2;
                             }
-                            if (yych <= 0x08)
-                            {
-                                goto basic_json_parser_30;
-                            }
-                            if (yych >= '\n')
+                            if (yych <= '!')
                             {
                                 goto basic_json_parser_4;
                             }
+                            goto basic_json_parser_9;
                         }
                         else
                         {
-                            if (yych == '\r')
+                            if (yych <= '+')
                             {
-                                goto basic_json_parser_2;
+                                goto basic_json_parser_4;
                             }
-                            if (yych <= 0x1F)
+                            if (yych <= ',')
                             {
-                                goto basic_json_parser_30;
+                                goto basic_json_parser_10;
                             }
+                            goto basic_json_parser_12;
                         }
                     }
                     else
                     {
-                        if (yych <= ',')
-                        {
-                            if (yych == '"')
-                            {
-                                goto basic_json_parser_27;
-                            }
-                            if (yych <= '+')
-                            {
-                                goto basic_json_parser_30;
-                            }
-                            goto basic_json_parser_16;
-                        }
-                        else
+                        if (yych <= '9')
                         {
                             if (yych <= '/')
                             {
-                                if (yych <= '-')
-                                {
-                                    goto basic_json_parser_23;
-                                }
-                                goto basic_json_parser_30;
+                                goto basic_json_parser_4;
                             }
-                            else
+                            if (yych <= '0')
                             {
-                                if (yych <= '0')
-                                {
-                                    goto basic_json_parser_24;
-                                }
-                                if (yych <= '9')
-                                {
-                                    goto basic_json_parser_26;
-                                }
-                                goto basic_json_parser_18;
+                                goto basic_json_parser_13;
                             }
+                            goto basic_json_parser_15;
+                        }
+                        else
+                        {
+                            if (yych <= ':')
+                            {
+                                goto basic_json_parser_17;
+                            }
+                            if (yych == '[')
+                            {
+                                goto basic_json_parser_19;
+                            }
+                            goto basic_json_parser_4;
                         }
                     }
                 }
                 else
                 {
-                    if (yych <= 'n')
+                    if (yych <= 't')
                     {
-                        if (yych <= ']')
+                        if (yych <= 'f')
                         {
-                            if (yych == '[')
+                            if (yych <= ']')
                             {
-                                goto basic_json_parser_8;
+                                goto basic_json_parser_21;
                             }
-                            if (yych <= '\\')
+                            if (yych <= 'e')
                             {
-                                goto basic_json_parser_30;
+                                goto basic_json_parser_4;
                             }
-                            goto basic_json_parser_10;
+                            goto basic_json_parser_23;
                         }
                         else
                         {
-                            if (yych == 'f')
+                            if (yych == 'n')
                             {
-                                goto basic_json_parser_22;
+                                goto basic_json_parser_24;
                             }
-                            if (yych <= 'm')
+                            if (yych <= 's')
                             {
-                                goto basic_json_parser_30;
+                                goto basic_json_parser_4;
                             }
-                            goto basic_json_parser_20;
+                            goto basic_json_parser_25;
                         }
                     }
                     else
                     {
-                        if (yych <= '{')
+                        if (yych <= '|')
                         {
-                            if (yych == 't')
+                            if (yych == '{')
                             {
-                                goto basic_json_parser_21;
+                                goto basic_json_parser_26;
                             }
-                            if (yych <= 'z')
-                            {
-                                goto basic_json_parser_30;
-                            }
-                            goto basic_json_parser_12;
+                            goto basic_json_parser_4;
                         }
                         else
                         {
                             if (yych <= '}')
                             {
-                                if (yych <= '|')
-                                {
-                                    goto basic_json_parser_30;
-                                }
-                                goto basic_json_parser_14;
+                                goto basic_json_parser_28;
                             }
-                            else
+                            if (yych == 0xEF)
                             {
-                                if (yych == 0xEF)
-                                {
-                                    goto basic_json_parser_6;
-                                }
                                 goto basic_json_parser_30;
                             }
+                            goto basic_json_parser_4;
                         }
                     }
                 }
 basic_json_parser_2:
                 ++m_cursor;
-                yych = *m_cursor;
-                goto basic_json_parser_5;
-basic_json_parser_3:
                 {
-                    return scan();
+                    return token_type::end_of_input;
                 }
 basic_json_parser_4:
                 ++m_cursor;
+basic_json_parser_5:
+                {
+                    return token_type::parse_error;
+                }
+basic_json_parser_6:
+                ++m_cursor;
                 if (m_limit <= m_cursor)
                 {
                     yyfill();    // LCOV_EXCL_LINE;
                 }
                 yych = *m_cursor;
-basic_json_parser_5:
                 if (yybm[0 + yych] & 32)
                 {
-                    goto basic_json_parser_4;
+                    goto basic_json_parser_6;
                 }
-                goto basic_json_parser_3;
-basic_json_parser_6:
+                {
+                    return scan();
+                }
+basic_json_parser_9:
                 yyaccept = 0;
                 yych = *(m_marker = ++m_cursor);
-                if (yych == 0xBB)
+                if (yych <= 0x0F)
                 {
-                    goto basic_json_parser_64;
+                    goto basic_json_parser_5;
                 }
-basic_json_parser_7:
-                {
-                    return token_type::parse_error;
-                }
-basic_json_parser_8:
-                ++m_cursor;
-                {
-                    return token_type::begin_array;
-                }
+                goto basic_json_parser_32;
 basic_json_parser_10:
                 ++m_cursor;
                 {
-                    return token_type::end_array;
-                }
-basic_json_parser_12:
-                ++m_cursor;
-                {
-                    return token_type::begin_object;
-                }
-basic_json_parser_14:
-                ++m_cursor;
-                {
-                    return token_type::end_object;
-                }
-basic_json_parser_16:
-                ++m_cursor;
-                {
                     return token_type::value_separator;
                 }
-basic_json_parser_18:
-                ++m_cursor;
-                {
-                    return token_type::name_separator;
-                }
-basic_json_parser_20:
-                yyaccept = 0;
-                yych = *(m_marker = ++m_cursor);
-                if (yych == 'u')
-                {
-                    goto basic_json_parser_60;
-                }
-                goto basic_json_parser_7;
-basic_json_parser_21:
-                yyaccept = 0;
-                yych = *(m_marker = ++m_cursor);
-                if (yych == 'r')
-                {
-                    goto basic_json_parser_56;
-                }
-                goto basic_json_parser_7;
-basic_json_parser_22:
-                yyaccept = 0;
-                yych = *(m_marker = ++m_cursor);
-                if (yych == 'a')
-                {
-                    goto basic_json_parser_51;
-                }
-                goto basic_json_parser_7;
-basic_json_parser_23:
+basic_json_parser_12:
                 yych = *++m_cursor;
                 if (yych <= '/')
                 {
-                    goto basic_json_parser_7;
+                    goto basic_json_parser_5;
                 }
                 if (yych <= '0')
                 {
-                    goto basic_json_parser_50;
+                    goto basic_json_parser_13;
                 }
                 if (yych <= '9')
                 {
-                    goto basic_json_parser_41;
+                    goto basic_json_parser_15;
                 }
-                goto basic_json_parser_7;
-basic_json_parser_24:
+                goto basic_json_parser_5;
+basic_json_parser_13:
                 yyaccept = 1;
                 yych = *(m_marker = ++m_cursor);
                 if (yych <= 'D')
                 {
                     if (yych == '.')
                     {
-                        goto basic_json_parser_43;
+                        goto basic_json_parser_37;
                     }
                 }
                 else
                 {
                     if (yych <= 'E')
                     {
-                        goto basic_json_parser_44;
+                        goto basic_json_parser_38;
                     }
                     if (yych == 'e')
                     {
-                        goto basic_json_parser_44;
+                        goto basic_json_parser_38;
                     }
                 }
-basic_json_parser_25:
+basic_json_parser_14:
                 {
                     return token_type::value_number;
                 }
-basic_json_parser_26:
+basic_json_parser_15:
                 yyaccept = 1;
-                yych = *(m_marker = ++m_cursor);
-                goto basic_json_parser_42;
-basic_json_parser_27:
+                m_marker = ++m_cursor;
+                if ((m_limit - m_cursor) < 3)
+                {
+                    yyfill();    // LCOV_EXCL_LINE;
+                }
+                yych = *m_cursor;
+                if (yybm[0 + yych] & 64)
+                {
+                    goto basic_json_parser_15;
+                }
+                if (yych <= 'D')
+                {
+                    if (yych == '.')
+                    {
+                        goto basic_json_parser_37;
+                    }
+                    goto basic_json_parser_14;
+                }
+                else
+                {
+                    if (yych <= 'E')
+                    {
+                        goto basic_json_parser_38;
+                    }
+                    if (yych == 'e')
+                    {
+                        goto basic_json_parser_38;
+                    }
+                    goto basic_json_parser_14;
+                }
+basic_json_parser_17:
+                ++m_cursor;
+                {
+                    return token_type::name_separator;
+                }
+basic_json_parser_19:
+                ++m_cursor;
+                {
+                    return token_type::begin_array;
+                }
+basic_json_parser_21:
+                ++m_cursor;
+                {
+                    return token_type::end_array;
+                }
+basic_json_parser_23:
                 yyaccept = 0;
                 yych = *(m_marker = ++m_cursor);
-                if (yych <= 0x0F)
+                if (yych == 'a')
                 {
-                    goto basic_json_parser_7;
+                    goto basic_json_parser_39;
                 }
-                goto basic_json_parser_32;
+                goto basic_json_parser_5;
+basic_json_parser_24:
+                yyaccept = 0;
+                yych = *(m_marker = ++m_cursor);
+                if (yych == 'u')
+                {
+                    goto basic_json_parser_40;
+                }
+                goto basic_json_parser_5;
+basic_json_parser_25:
+                yyaccept = 0;
+                yych = *(m_marker = ++m_cursor);
+                if (yych == 'r')
+                {
+                    goto basic_json_parser_41;
+                }
+                goto basic_json_parser_5;
+basic_json_parser_26:
+                ++m_cursor;
+                {
+                    return token_type::begin_object;
+                }
 basic_json_parser_28:
                 ++m_cursor;
                 {
-                    return token_type::end_of_input;
+                    return token_type::end_object;
                 }
 basic_json_parser_30:
-                yych = *++m_cursor;
-                goto basic_json_parser_7;
+                yyaccept = 0;
+                yych = *(m_marker = ++m_cursor);
+                if (yych == 0xBB)
+                {
+                    goto basic_json_parser_42;
+                }
+                goto basic_json_parser_5;
 basic_json_parser_31:
                 ++m_cursor;
                 if (m_limit <= m_cursor)
@@ -7740,7 +7674,7 @@
                 }
                 yych = *m_cursor;
 basic_json_parser_32:
-                if (yybm[0 + yych] & 64)
+                if (yybm[0 + yych] & 128)
                 {
                     goto basic_json_parser_31;
                 }
@@ -7750,21 +7684,26 @@
                 }
                 if (yych <= '"')
                 {
-                    goto basic_json_parser_35;
+                    goto basic_json_parser_34;
                 }
-                goto basic_json_parser_34;
+                goto basic_json_parser_36;
 basic_json_parser_33:
                 m_cursor = m_marker;
                 if (yyaccept == 0)
                 {
-                    goto basic_json_parser_7;
+                    goto basic_json_parser_5;
                 }
                 else
                 {
-                    goto basic_json_parser_25;
+                    goto basic_json_parser_14;
                 }
 basic_json_parser_34:
                 ++m_cursor;
+                {
+                    return token_type::value_string;
+                }
+basic_json_parser_36:
+                ++m_cursor;
                 if (m_limit <= m_cursor)
                 {
                     yyfill();    // LCOV_EXCL_LINE;
@@ -7836,18 +7775,78 @@
                             }
                             if (yych <= 'u')
                             {
-                                goto basic_json_parser_37;
+                                goto basic_json_parser_43;
                             }
                             goto basic_json_parser_33;
                         }
                     }
                 }
-basic_json_parser_35:
-                ++m_cursor;
-                {
-                    return token_type::value_string;
-                }
 basic_json_parser_37:
+                yych = *++m_cursor;
+                if (yych <= '/')
+                {
+                    goto basic_json_parser_33;
+                }
+                if (yych <= '9')
+                {
+                    goto basic_json_parser_44;
+                }
+                goto basic_json_parser_33;
+basic_json_parser_38:
+                yych = *++m_cursor;
+                if (yych <= ',')
+                {
+                    if (yych == '+')
+                    {
+                        goto basic_json_parser_46;
+                    }
+                    goto basic_json_parser_33;
+                }
+                else
+                {
+                    if (yych <= '-')
+                    {
+                        goto basic_json_parser_46;
+                    }
+                    if (yych <= '/')
+                    {
+                        goto basic_json_parser_33;
+                    }
+                    if (yych <= '9')
+                    {
+                        goto basic_json_parser_47;
+                    }
+                    goto basic_json_parser_33;
+                }
+basic_json_parser_39:
+                yych = *++m_cursor;
+                if (yych == 'l')
+                {
+                    goto basic_json_parser_49;
+                }
+                goto basic_json_parser_33;
+basic_json_parser_40:
+                yych = *++m_cursor;
+                if (yych == 'l')
+                {
+                    goto basic_json_parser_50;
+                }
+                goto basic_json_parser_33;
+basic_json_parser_41:
+                yych = *++m_cursor;
+                if (yych == 'u')
+                {
+                    goto basic_json_parser_51;
+                }
+                goto basic_json_parser_33;
+basic_json_parser_42:
+                yych = *++m_cursor;
+                if (yych == 0xBF)
+                {
+                    goto basic_json_parser_52;
+                }
+                goto basic_json_parser_33;
+basic_json_parser_43:
                 ++m_cursor;
                 if (m_limit <= m_cursor)
                 {
@@ -7860,27 +7859,113 @@
                     {
                         goto basic_json_parser_33;
                     }
-                    if (yych >= ':')
+                    if (yych <= '9')
                     {
-                        goto basic_json_parser_33;
+                        goto basic_json_parser_54;
                     }
+                    goto basic_json_parser_33;
                 }
                 else
                 {
                     if (yych <= 'F')
                     {
+                        goto basic_json_parser_54;
+                    }
+                    if (yych <= '`')
+                    {
+                        goto basic_json_parser_33;
+                    }
+                    if (yych <= 'f')
+                    {
+                        goto basic_json_parser_54;
+                    }
+                    goto basic_json_parser_33;
+                }
+basic_json_parser_44:
+                yyaccept = 1;
+                m_marker = ++m_cursor;
+                if ((m_limit - m_cursor) < 3)
+                {
+                    yyfill();    // LCOV_EXCL_LINE;
+                }
+                yych = *m_cursor;
+                if (yych <= 'D')
+                {
+                    if (yych <= '/')
+                    {
+                        goto basic_json_parser_14;
+                    }
+                    if (yych <= '9')
+                    {
+                        goto basic_json_parser_44;
+                    }
+                    goto basic_json_parser_14;
+                }
+                else
+                {
+                    if (yych <= 'E')
+                    {
                         goto basic_json_parser_38;
                     }
-                    if (yych <= '`')
+                    if (yych == 'e')
                     {
-                        goto basic_json_parser_33;
+                        goto basic_json_parser_38;
                     }
-                    if (yych >= 'g')
-                    {
-                        goto basic_json_parser_33;
-                    }
+                    goto basic_json_parser_14;
                 }
-basic_json_parser_38:
+basic_json_parser_46:
+                yych = *++m_cursor;
+                if (yych <= '/')
+                {
+                    goto basic_json_parser_33;
+                }
+                if (yych >= ':')
+                {
+                    goto basic_json_parser_33;
+                }
+basic_json_parser_47:
+                ++m_cursor;
+                if (m_limit <= m_cursor)
+                {
+                    yyfill();    // LCOV_EXCL_LINE;
+                }
+                yych = *m_cursor;
+                if (yych <= '/')
+                {
+                    goto basic_json_parser_14;
+                }
+                if (yych <= '9')
+                {
+                    goto basic_json_parser_47;
+                }
+                goto basic_json_parser_14;
+basic_json_parser_49:
+                yych = *++m_cursor;
+                if (yych == 's')
+                {
+                    goto basic_json_parser_55;
+                }
+                goto basic_json_parser_33;
+basic_json_parser_50:
+                yych = *++m_cursor;
+                if (yych == 'l')
+                {
+                    goto basic_json_parser_56;
+                }
+                goto basic_json_parser_33;
+basic_json_parser_51:
+                yych = *++m_cursor;
+                if (yych == 'e')
+                {
+                    goto basic_json_parser_58;
+                }
+                goto basic_json_parser_33;
+basic_json_parser_52:
+                ++m_cursor;
+                {
+                    return scan();
+                }
+basic_json_parser_54:
                 ++m_cursor;
                 if (m_limit <= m_cursor)
                 {
@@ -7893,27 +7978,46 @@
                     {
                         goto basic_json_parser_33;
                     }
-                    if (yych >= ':')
+                    if (yych <= '9')
                     {
-                        goto basic_json_parser_33;
+                        goto basic_json_parser_60;
                     }
+                    goto basic_json_parser_33;
                 }
                 else
                 {
                     if (yych <= 'F')
                     {
-                        goto basic_json_parser_39;
+                        goto basic_json_parser_60;
                     }
                     if (yych <= '`')
                     {
                         goto basic_json_parser_33;
                     }
-                    if (yych >= 'g')
+                    if (yych <= 'f')
                     {
-                        goto basic_json_parser_33;
+                        goto basic_json_parser_60;
                     }
+                    goto basic_json_parser_33;
                 }
-basic_json_parser_39:
+basic_json_parser_55:
+                yych = *++m_cursor;
+                if (yych == 'e')
+                {
+                    goto basic_json_parser_61;
+                }
+                goto basic_json_parser_33;
+basic_json_parser_56:
+                ++m_cursor;
+                {
+                    return token_type::literal_null;
+                }
+basic_json_parser_58:
+                ++m_cursor;
+                {
+                    return token_type::literal_true;
+                }
+basic_json_parser_60:
                 ++m_cursor;
                 if (m_limit <= m_cursor)
                 {
@@ -7926,27 +8030,34 @@
                     {
                         goto basic_json_parser_33;
                     }
-                    if (yych >= ':')
+                    if (yych <= '9')
                     {
-                        goto basic_json_parser_33;
+                        goto basic_json_parser_63;
                     }
+                    goto basic_json_parser_33;
                 }
                 else
                 {
                     if (yych <= 'F')
                     {
-                        goto basic_json_parser_40;
+                        goto basic_json_parser_63;
                     }
                     if (yych <= '`')
                     {
                         goto basic_json_parser_33;
                     }
-                    if (yych >= 'g')
+                    if (yych <= 'f')
                     {
-                        goto basic_json_parser_33;
+                        goto basic_json_parser_63;
                     }
+                    goto basic_json_parser_33;
                 }
-basic_json_parser_40:
+basic_json_parser_61:
+                ++m_cursor;
+                {
+                    return token_type::literal_false;
+                }
+basic_json_parser_63:
                 ++m_cursor;
                 if (m_limit <= m_cursor)
                 {
@@ -7981,215 +8092,6 @@
                     }
                     goto basic_json_parser_33;
                 }
-basic_json_parser_41:
-                yyaccept = 1;
-                m_marker = ++m_cursor;
-                if ((m_limit - m_cursor) < 3)
-                {
-                    yyfill();    // LCOV_EXCL_LINE;
-                }
-                yych = *m_cursor;
-basic_json_parser_42:
-                if (yybm[0 + yych] & 128)
-                {
-                    goto basic_json_parser_41;
-                }
-                if (yych <= 'D')
-                {
-                    if (yych != '.')
-                    {
-                        goto basic_json_parser_25;
-                    }
-                }
-                else
-                {
-                    if (yych <= 'E')
-                    {
-                        goto basic_json_parser_44;
-                    }
-                    if (yych == 'e')
-                    {
-                        goto basic_json_parser_44;
-                    }
-                    goto basic_json_parser_25;
-                }
-basic_json_parser_43:
-                yych = *++m_cursor;
-                if (yych <= '/')
-                {
-                    goto basic_json_parser_33;
-                }
-                if (yych <= '9')
-                {
-                    goto basic_json_parser_48;
-                }
-                goto basic_json_parser_33;
-basic_json_parser_44:
-                yych = *++m_cursor;
-                if (yych <= ',')
-                {
-                    if (yych != '+')
-                    {
-                        goto basic_json_parser_33;
-                    }
-                }
-                else
-                {
-                    if (yych <= '-')
-                    {
-                        goto basic_json_parser_45;
-                    }
-                    if (yych <= '/')
-                    {
-                        goto basic_json_parser_33;
-                    }
-                    if (yych <= '9')
-                    {
-                        goto basic_json_parser_46;
-                    }
-                    goto basic_json_parser_33;
-                }
-basic_json_parser_45:
-                yych = *++m_cursor;
-                if (yych <= '/')
-                {
-                    goto basic_json_parser_33;
-                }
-                if (yych >= ':')
-                {
-                    goto basic_json_parser_33;
-                }
-basic_json_parser_46:
-                ++m_cursor;
-                if (m_limit <= m_cursor)
-                {
-                    yyfill();    // LCOV_EXCL_LINE;
-                }
-                yych = *m_cursor;
-                if (yych <= '/')
-                {
-                    goto basic_json_parser_25;
-                }
-                if (yych <= '9')
-                {
-                    goto basic_json_parser_46;
-                }
-                goto basic_json_parser_25;
-basic_json_parser_48:
-                yyaccept = 1;
-                m_marker = ++m_cursor;
-                if ((m_limit - m_cursor) < 3)
-                {
-                    yyfill();    // LCOV_EXCL_LINE;
-                }
-                yych = *m_cursor;
-                if (yych <= 'D')
-                {
-                    if (yych <= '/')
-                    {
-                        goto basic_json_parser_25;
-                    }
-                    if (yych <= '9')
-                    {
-                        goto basic_json_parser_48;
-                    }
-                    goto basic_json_parser_25;
-                }
-                else
-                {
-                    if (yych <= 'E')
-                    {
-                        goto basic_json_parser_44;
-                    }
-                    if (yych == 'e')
-                    {
-                        goto basic_json_parser_44;
-                    }
-                    goto basic_json_parser_25;
-                }
-basic_json_parser_50:
-                yyaccept = 1;
-                yych = *(m_marker = ++m_cursor);
-                if (yych <= 'D')
-                {
-                    if (yych == '.')
-                    {
-                        goto basic_json_parser_43;
-                    }
-                    goto basic_json_parser_25;
-                }
-                else
-                {
-                    if (yych <= 'E')
-                    {
-                        goto basic_json_parser_44;
-                    }
-                    if (yych == 'e')
-                    {
-                        goto basic_json_parser_44;
-                    }
-                    goto basic_json_parser_25;
-                }
-basic_json_parser_51:
-                yych = *++m_cursor;
-                if (yych != 'l')
-                {
-                    goto basic_json_parser_33;
-                }
-                yych = *++m_cursor;
-                if (yych != 's')
-                {
-                    goto basic_json_parser_33;
-                }
-                yych = *++m_cursor;
-                if (yych != 'e')
-                {
-                    goto basic_json_parser_33;
-                }
-                ++m_cursor;
-                {
-                    return token_type::literal_false;
-                }
-basic_json_parser_56:
-                yych = *++m_cursor;
-                if (yych != 'u')
-                {
-                    goto basic_json_parser_33;
-                }
-                yych = *++m_cursor;
-                if (yych != 'e')
-                {
-                    goto basic_json_parser_33;
-                }
-                ++m_cursor;
-                {
-                    return token_type::literal_true;
-                }
-basic_json_parser_60:
-                yych = *++m_cursor;
-                if (yych != 'l')
-                {
-                    goto basic_json_parser_33;
-                }
-                yych = *++m_cursor;
-                if (yych != 'l')
-                {
-                    goto basic_json_parser_33;
-                }
-                ++m_cursor;
-                {
-                    return token_type::literal_null;
-                }
-basic_json_parser_64:
-                yych = *++m_cursor;
-                if (yych != 0xBF)
-                {
-                    goto basic_json_parser_33;
-                }
-                ++m_cursor;
-                {
-                    return scan();
-                }
             }
 
         }
@@ -8240,13 +8142,14 @@
 
         1. Escaped characters. In this case, a new character is constructed
            according to the nature of the escape. Some escapes create new
-           characters (e.g., `"\\n"` is replaced by `"\n"`), some are copied as
-           is (e.g., `"\\\\"`). Furthermore, Unicode escapes of the shape
+           characters (e.g., `"\\n"` is replaced by `"\n"`), some are copied
+           as is (e.g., `"\\\\"`). Furthermore, Unicode escapes of the shape
            `"\\uxxxx"` need special care. In this case, to_unicode takes care
            of the construction of the values.
         2. Unescaped characters are copied as is.
 
-        @return string value of current token without opening and closing quotes
+        @return string value of current token without opening and closing
+        quotes
         @throw std::out_of_range if to_unicode fails
         */
         string_t get_string() const
@@ -8357,8 +8260,8 @@
 
         This function (and its overloads) serves to select the most approprate
         standard floating point number parsing function based on the type
-        supplied via the first parameter.  Set this to
-        @a static_cast<number_float_t*>(nullptr).
+        supplied via the first parameter.  Set this to @a
+        static_cast<number_float_t*>(nullptr).
 
         @param[in] type  the @ref number_float_t in use
 
@@ -8382,8 +8285,8 @@
 
         This function (and its overloads) serves to select the most approprate
         standard floating point number parsing function based on the type
-        supplied via the first parameter.  Set this to
-        @a static_cast<number_float_t*>(nullptr).
+        supplied via the first parameter.  Set this to @a
+        static_cast<number_float_t*>(nullptr).
 
         @param[in] type  the @ref number_float_t in use
 
@@ -8402,8 +8305,8 @@
 
         This function (and its overloads) serves to select the most approprate
         standard floating point number parsing function based on the type
-        supplied via the first parameter.  Set this to
-        @a static_cast<number_float_t*>(nullptr).
+        supplied via the first parameter.  Set this to @a
+        static_cast<number_float_t*>(nullptr).
 
         @param[in] type  the @ref number_float_t in use
 
@@ -8427,22 +8330,16 @@
         This function parses the integer component up to the radix point or
         exponent while collecting information about the 'floating point
         representation', which it stores in the result parameter. If there is
-        no radix point or exponent, and the number can fit into a
-        @ref number_integer_t or @ref number_unsigned_t then it sets the
-        result parameter accordingly.
-
-        The 'floating point representation' includes the number of significant
-        figures after the radix point, whether the number is in exponential
-        or decimal form, the capitalization of the exponent marker, and if the
-        optional '+' is present in the exponent. This information is necessary
-        to perform accurate round trips of floating point numbers.
+        no radix point or exponent, and the number can fit into a @ref
+        number_integer_t or @ref number_unsigned_t then it sets the result
+        parameter accordingly.
 
         If the number is a floating point number the number is then parsed
         using @a std:strtod (or @a std:strtof or @a std::strtold).
 
         @param[out] result  @ref basic_json object to receive the number, or
-          NAN if the conversion read past the current token. The latter case
-          needs to be treated by the caller function.
+        NAN if the conversion read past the current token. The latter case
+        needs to be treated by the caller function.
         */
         void get_number(basic_json& result) const
         {
@@ -8450,15 +8347,6 @@
 
             const lexer::lexer_char_t* curptr = m_start;
 
-            // remember this number was parsed (for later serialization)
-            result.m_type.bits.parsed = true;
-
-            // 'found_radix_point' will be set to 0xFF upon finding a radix
-            // point and later used to mask in/out the precision depending
-            // whether a radix is found i.e. 'precision &= found_radix_point'
-            uint8_t found_radix_point = 0;
-            uint8_t precision = 0;
-
             // accumulate the integer conversion result (unsigned for now)
             number_unsigned_t value = 0;
 
@@ -8472,17 +8360,13 @@
             if (*curptr == '-')
             {
                 type = value_t::number_integer;
-                max = static_cast<uint64_t>(std::numeric_limits<number_integer_t>::max()) + 1;
+                max = static_cast<uint64_t>((std::numeric_limits<number_integer_t>::max)()) + 1;
                 curptr++;
             }
             else
             {
                 type = value_t::number_unsigned;
-                max = static_cast<uint64_t>(std::numeric_limits<number_unsigned_t>::max());
-                if (*curptr == '+')
-                {
-                    curptr++;
-                }
+                max = static_cast<uint64_t>((std::numeric_limits<number_unsigned_t>::max)());
             }
 
             // count the significant figures
@@ -8495,22 +8379,11 @@
                     {
                         // don't count '.' but change to float
                         type = value_t::number_float;
-
-                        // reset precision count
-                        precision = 0;
-                        found_radix_point = 0xFF;
                         continue;
                     }
                     // assume exponent (if not then will fail parse): change to
                     // float, stop counting and record exponent details
                     type = value_t::number_float;
-                    result.m_type.bits.has_exp = true;
-
-                    // exponent capitalization
-                    result.m_type.bits.exp_cap = (*curptr == 'E');
-
-                    // exponent '+' sign
-                    result.m_type.bits.exp_plus = (*(++curptr) == '+');
                     break;
                 }
 
@@ -8532,13 +8405,8 @@
                         value = temp;
                     }
                 }
-                ++precision;
             }
 
-            // If no radix point was found then precision would now be set to
-            // the number of digits, which is wrong - clear it.
-            result.m_type.bits.precision = precision & found_radix_point;
-
             // save the value (if not a float)
             if (type == value_t::number_unsigned)
             {
@@ -8844,6 +8712,1232 @@
         /// the lexer
         lexer m_lexer;
     };
+
+  public:
+    /*!
+    @brief JSON Pointer
+
+    A JSON pointer defines a string syntax for identifying a specific value
+    within a JSON document. It can be used with functions `at` and
+    `operator[]`. Furthermore, JSON pointers are the base for JSON patches.
+
+    @sa [RFC 6901](https://tools.ietf.org/html/rfc6901)
+
+    @since version 2.0.0
+    */
+    class json_pointer
+    {
+        /// allow basic_json to access private members
+        friend class basic_json;
+
+      public:
+        /*!
+        @brief create JSON pointer
+
+        Create a JSON pointer according to the syntax described in
+        [Section 3 of RFC6901](https://tools.ietf.org/html/rfc6901#section-3).
+
+        @param[in] s  string representing the JSON pointer; if omitted, the
+                      empty string is assumed which references the whole JSON
+                      value
+
+        @throw std::domain_error if reference token is nonempty and does not
+        begin with a slash (`/`); example: `"JSON pointer must be empty or
+        begin with /"`
+        @throw std::domain_error if a tilde (`~`) is not followed by `0`
+        (representing `~`) or `1` (representing `/`); example: `"escape error:
+        ~ must be followed with 0 or 1"`
+
+        @liveexample{The example shows the construction several valid JSON
+        pointers as well as the exceptional behavior.,json_pointer}
+
+        @since version 2.0.0
+        */
+        explicit json_pointer(const std::string& s = "")
+            : reference_tokens(split(s))
+        {}
+
+        /*!
+        @brief return a string representation of the JSON pointer
+
+        @invariant For each JSON pointer `ptr`, it holds:
+        @code {.cpp}
+        ptr == json_pointer(ptr.to_string());
+        @endcode
+
+        @return a string representation of the JSON pointer
+
+        @liveexample{The example shows the result of `to_string`.,
+        json_pointer__to_string}
+
+        @since version 2.0.0
+        */
+        std::string to_string() const noexcept
+        {
+            std::string result;
+
+            for (const auto& reference_token : reference_tokens)
+            {
+                result += "/" + escape(reference_token);
+            }
+
+            return result;
+        }
+
+        /// @copydoc to_string()
+        operator std::string() const
+        {
+            return to_string();
+        }
+
+      private:
+        /// remove and return last reference pointer
+        std::string pop_back()
+        {
+            if (is_root())
+            {
+                throw std::domain_error("JSON pointer has no parent");
+            }
+
+            auto last = reference_tokens.back();
+            reference_tokens.pop_back();
+            return last;
+        }
+
+        /// return whether pointer points to the root document
+        bool is_root() const
+        {
+            return reference_tokens.empty();
+        }
+
+        json_pointer top() const
+        {
+            if (is_root())
+            {
+                throw std::domain_error("JSON pointer has no parent");
+            }
+
+            json_pointer result = *this;
+            result.reference_tokens = {reference_tokens[0]};
+            return result;
+        }
+
+        /*!
+        @brief create and return a reference to the pointed to value
+        */
+        reference get_and_create(reference j) const
+        {
+            pointer result = &j;
+
+            // in case no reference tokens exist, return a reference to the
+            // JSON value j which will be overwritten by a primitive value
+            for (const auto& reference_token : reference_tokens)
+            {
+                switch (result->m_type)
+                {
+                    case value_t::null:
+                    {
+                        if (reference_token == "0")
+                        {
+                            // start a new array if reference token is 0
+                            result = &result->operator[](0);
+                        }
+                        else
+                        {
+                            // start a new object otherwise
+                            result = &result->operator[](reference_token);
+                        }
+                        break;
+                    }
+
+                    case value_t::object:
+                    {
+                        // create an entry in the object
+                        result = &result->operator[](reference_token);
+                        break;
+                    }
+
+                    case value_t::array:
+                    {
+                        // create an entry in the array
+                        result = &result->operator[](static_cast<size_type>(std::stoi(reference_token)));
+                        break;
+                    }
+
+                    /*
+                    The following code is only reached if there exists a
+                    reference token _and_ the current value is primitive. In
+                    this case, we have an error situation, because primitive
+                    values may only occur as single value; that is, with an
+                    empty list of reference tokens.
+                    */
+                    default:
+                    {
+                        throw std::domain_error("invalid value to unflatten");
+                    }
+                }
+            }
+
+            return *result;
+        }
+
+        /*!
+        @brief return a reference to the pointed to value
+
+        @param[in] ptr  a JSON value
+
+        @return reference to the JSON value pointed to by the JSON pointer
+
+        @complexity Linear in the length of the JSON pointer.
+
+        @throw std::out_of_range      if the JSON pointer can not be resolved
+        @throw std::domain_error      if an array index begins with '0'
+        @throw std::invalid_argument  if an array index was not a number
+        */
+        reference get_unchecked(pointer ptr) const
+        {
+            for (const auto& reference_token : reference_tokens)
+            {
+                switch (ptr->m_type)
+                {
+                    case value_t::object:
+                    {
+                        // use unchecked object access
+                        ptr = &ptr->operator[](reference_token);
+                        break;
+                    }
+
+                    case value_t::array:
+                    {
+                        // error condition (cf. RFC 6901, Sect. 4)
+                        if (reference_token.size() > 1 and reference_token[0] == '0')
+                        {
+                            throw std::domain_error("array index must not begin with '0'");
+                        }
+
+                        if (reference_token == "-")
+                        {
+                            // explicityly treat "-" as index beyond the end
+                            ptr = &ptr->operator[](ptr->m_value.array->size());
+                        }
+                        else
+                        {
+                            // convert array index to number; unchecked access
+                            ptr = &ptr->operator[](static_cast<size_type>(std::stoi(reference_token)));
+                        }
+                        break;
+                    }
+
+                    default:
+                    {
+                        throw std::out_of_range("unresolved reference token '" + reference_token + "'");
+                    }
+                }
+            }
+
+            return *ptr;
+        }
+
+        reference get_checked(pointer ptr) const
+        {
+            for (const auto& reference_token : reference_tokens)
+            {
+                switch (ptr->m_type)
+                {
+                    case value_t::object:
+                    {
+                        // note: at performs range check
+                        ptr = &ptr->at(reference_token);
+                        break;
+                    }
+
+                    case value_t::array:
+                    {
+                        if (reference_token == "-")
+                        {
+                            // "-" always fails the range check
+                            throw std::out_of_range("array index '-' (" +
+                                                    std::to_string(ptr->m_value.array->size()) +
+                                                    ") is out of range");
+                        }
+
+                        // error condition (cf. RFC 6901, Sect. 4)
+                        if (reference_token.size() > 1 and reference_token[0] == '0')
+                        {
+                            throw std::domain_error("array index must not begin with '0'");
+                        }
+
+                        // note: at performs range check
+                        ptr = &ptr->at(static_cast<size_type>(std::stoi(reference_token)));
+                        break;
+                    }
+
+                    default:
+                    {
+                        throw std::out_of_range("unresolved reference token '" + reference_token + "'");
+                    }
+                }
+            }
+
+            return *ptr;
+        }
+
+        /*!
+        @brief return a const reference to the pointed to value
+
+        @param[in] ptr  a JSON value
+
+        @return const reference to the JSON value pointed to by the JSON
+                pointer
+        */
+        const_reference get_unchecked(const_pointer ptr) const
+        {
+            for (const auto& reference_token : reference_tokens)
+            {
+                switch (ptr->m_type)
+                {
+                    case value_t::object:
+                    {
+                        // use unchecked object access
+                        ptr = &ptr->operator[](reference_token);
+                        break;
+                    }
+
+                    case value_t::array:
+                    {
+                        if (reference_token == "-")
+                        {
+                            // "-" cannot be used for const access
+                            throw std::out_of_range("array index '-' (" +
+                                                    std::to_string(ptr->m_value.array->size()) +
+                                                    ") is out of range");
+                        }
+
+                        // error condition (cf. RFC 6901, Sect. 4)
+                        if (reference_token.size() > 1 and reference_token[0] == '0')
+                        {
+                            throw std::domain_error("array index must not begin with '0'");
+                        }
+
+                        // use unchecked array access
+                        ptr = &ptr->operator[](static_cast<size_type>(std::stoi(reference_token)));
+                        break;
+                    }
+
+                    default:
+                    {
+                        throw std::out_of_range("unresolved reference token '" + reference_token + "'");
+                    }
+                }
+            }
+
+            return *ptr;
+        }
+
+        const_reference get_checked(const_pointer ptr) const
+        {
+            for (const auto& reference_token : reference_tokens)
+            {
+                switch (ptr->m_type)
+                {
+                    case value_t::object:
+                    {
+                        // note: at performs range check
+                        ptr = &ptr->at(reference_token);
+                        break;
+                    }
+
+                    case value_t::array:
+                    {
+                        if (reference_token == "-")
+                        {
+                            // "-" always fails the range check
+                            throw std::out_of_range("array index '-' (" +
+                                                    std::to_string(ptr->m_value.array->size()) +
+                                                    ") is out of range");
+                        }
+
+                        // error condition (cf. RFC 6901, Sect. 4)
+                        if (reference_token.size() > 1 and reference_token[0] == '0')
+                        {
+                            throw std::domain_error("array index must not begin with '0'");
+                        }
+
+                        // note: at performs range check
+                        ptr = &ptr->at(static_cast<size_type>(std::stoi(reference_token)));
+                        break;
+                    }
+
+                    default:
+                    {
+                        throw std::out_of_range("unresolved reference token '" + reference_token + "'");
+                    }
+                }
+            }
+
+            return *ptr;
+        }
+
+        /// split the string input to reference tokens
+        static std::vector<std::string> split(std::string reference_string)
+        {
+            std::vector<std::string> result;
+
+            // special case: empty reference string -> no reference tokens
+            if (reference_string.empty())
+            {
+                return result;
+            }
+
+            // check if nonempty reference string begins with slash
+            if (reference_string[0] != '/')
+            {
+                throw std::domain_error("JSON pointer must be empty or begin with '/'");
+            }
+
+            // extract the reference tokens:
+            // - slash: position of the last read slash (or end of string)
+            // - start: position after the previous slash
+            for (
+                // search for the first slash after the first character
+                size_t slash = reference_string.find_first_of("/", 1),
+                // set the beginning of the first reference token
+                start = 1;
+                // we can stop if start == string::npos+1 = 0
+                start != 0;
+                // set the beginning of the next reference token
+                // (will eventually be 0 if slash == std::string::npos)
+                start = slash + 1,
+                // find next slash
+                slash = reference_string.find_first_of("/", start))
+            {
+                // use the text between the beginning of the reference token
+                // (start) and the last slash (slash).
+                auto reference_token = reference_string.substr(start, slash - start);
+
+                // check reference tokens are properly escaped
+                for (size_t pos = reference_token.find_first_of("~");
+                        pos != std::string::npos;
+                        pos = reference_token.find_first_of("~", pos + 1))
+                {
+                    assert(reference_token[pos] == '~');
+
+                    // ~ must be followed by 0 or 1
+                    if (pos == reference_token.size() - 1 or
+                            (reference_token[pos + 1] != '0' and
+                             reference_token[pos + 1] != '1'))
+                    {
+                        throw std::domain_error("escape error: '~' must be followed with '0' or '1'");
+                    }
+                }
+
+                // finally, store the reference token
+                unescape(reference_token);
+                result.push_back(reference_token);
+            }
+
+            return result;
+        }
+
+      private:
+        /*!
+        @brief replace all occurrences of a substring by another string
+
+        @param[in,out] s  the string to manipulate
+        @param[in]     f  the substring to replace with @a t
+        @param[out]    t  the string to replace @a f
+
+        @return The string @a s where all occurrences of @a f are replaced
+                with @a t.
+
+        @pre The search string @a f must not be empty.
+
+        @since version 2.0.0
+        */
+        static void replace_substring(std::string& s,
+                                      const std::string& f,
+                                      const std::string& t)
+        {
+            assert(not f.empty());
+
+            for (
+                size_t pos = s.find(f);         // find first occurrence of f
+                pos != std::string::npos;       // make sure f was found
+                s.replace(pos, f.size(), t),    // replace with t
+                pos = s.find(f, pos + t.size()) // find next occurrence of f
+            );
+        }
+
+        /// escape tilde and slash
+        static std::string escape(std::string s)
+        {
+            // escape "~"" to "~0" and "/" to "~1"
+            replace_substring(s, "~", "~0");
+            replace_substring(s, "/", "~1");
+            return s;
+        }
+
+        /// unescape tilde and slash
+        static void unescape(std::string& s)
+        {
+            // first transform any occurrence of the sequence '~1' to '/'
+            replace_substring(s, "~1", "/");
+            // then transform any occurrence of the sequence '~0' to '~'
+            replace_substring(s, "~0", "~");
+        }
+
+        /*!
+        @param[in] reference_string  the reference string to the current value
+        @param[in] value             the value to consider
+        @param[in,out] result        the result object to insert values to
+
+        @note Empty objects or arrays are flattened to `null`.
+        */
+        static void flatten(const std::string& reference_string,
+                            const basic_json& value,
+                            basic_json& result)
+        {
+            switch (value.m_type)
+            {
+                case value_t::array:
+                {
+                    if (value.m_value.array->empty())
+                    {
+                        // flatten empty array as null
+                        result[reference_string] = nullptr;
+                    }
+                    else
+                    {
+                        // iterate array and use index as reference string
+                        for (size_t i = 0; i < value.m_value.array->size(); ++i)
+                        {
+                            flatten(reference_string + "/" + std::to_string(i),
+                                    value.m_value.array->operator[](i), result);
+                        }
+                    }
+                    break;
+                }
+
+                case value_t::object:
+                {
+                    if (value.m_value.object->empty())
+                    {
+                        // flatten empty object as null
+                        result[reference_string] = nullptr;
+                    }
+                    else
+                    {
+                        // iterate object and use keys as reference string
+                        for (const auto& element : *value.m_value.object)
+                        {
+                            flatten(reference_string + "/" + escape(element.first),
+                                    element.second, result);
+                        }
+                    }
+                    break;
+                }
+
+                default:
+                {
+                    // add primitive value with its reference string
+                    result[reference_string] = value;
+                    break;
+                }
+            }
+        }
+
+        /*!
+        @param[in] value  flattened JSON
+
+        @return unflattened JSON
+        */
+        static basic_json unflatten(const basic_json& value)
+        {
+            if (not value.is_object())
+            {
+                throw std::domain_error("only objects can be unflattened");
+            }
+
+            basic_json result;
+
+            // iterate the JSON object values
+            for (const auto& element : *value.m_value.object)
+            {
+                if (not element.second.is_primitive())
+                {
+                    throw std::domain_error("values in object must be primitive");
+                }
+
+                // assign value to reference pointed to by JSON pointer; Note
+                // that if the JSON pointer is "" (i.e., points to the whole
+                // value), function get_and_create returns a reference to
+                // result itself. An assignment will then create a primitive
+                // value.
+                json_pointer(element.first).get_and_create(result) = element.second;
+            }
+
+            return result;
+        }
+
+      private:
+        /// the reference tokens
+        std::vector<std::string> reference_tokens {};
+    };
+
+    //////////////////////////
+    // JSON Pointer support //
+    //////////////////////////
+
+    /// @name JSON Pointer functions
+    /// @{
+
+    /*!
+    @brief access specified element via JSON Pointer
+
+    Uses a JSON pointer to retrieve a reference to the respective JSON value.
+    No bound checking is performed. Similar to @ref operator[](const typename
+    object_t::key_type&), `null` values are created in arrays and objects if
+    necessary.
+
+    In particular:
+    - If the JSON pointer points to an object key that does not exist, it
+      is created an filled with a `null` value before a reference to it
+      is returned.
+    - If the JSON pointer points to an array index that does not exist, it
+      is created an filled with a `null` value before a reference to it
+      is returned. All indices between the current maximum and the given
+      index are also filled with `null`.
+    - The special value `-` is treated as a synonym for the index past the
+      end.
+
+    @param[in] ptr  a JSON pointer
+
+    @return reference to the element pointed to by @a ptr
+
+    @complexity Constant.
+
+    @throw std::out_of_range      if the JSON pointer can not be resolved
+    @throw std::domain_error      if an array index begins with '0'
+    @throw std::invalid_argument  if an array index was not a number
+
+    @liveexample{The behavior is shown in the example.,operatorjson_pointer}
+
+    @since version 2.0.0
+    */
+    reference operator[](const json_pointer& ptr)
+    {
+        return ptr.get_unchecked(this);
+    }
+
+    /*!
+    @brief access specified element via JSON Pointer
+
+    Uses a JSON pointer to retrieve a reference to the respective JSON value.
+    No bound checking is performed. The function does not change the JSON
+    value; no `null` values are created. In particular, the the special value
+    `-` yields an exception.
+
+    @param[in] ptr  JSON pointer to the desired element
+
+    @return const reference to the element pointed to by @a ptr
+
+    @complexity Constant.
+
+    @throw std::out_of_range      if the JSON pointer can not be resolved
+    @throw std::domain_error      if an array index begins with '0'
+    @throw std::invalid_argument  if an array index was not a number
+
+    @liveexample{The behavior is shown in the example.,operatorjson_pointer_const}
+
+    @since version 2.0.0
+    */
+    const_reference operator[](const json_pointer& ptr) const
+    {
+        return ptr.get_unchecked(this);
+    }
+
+    /*!
+    @brief access specified element via JSON Pointer
+
+    Returns a reference to the element at with specified JSON pointer @a ptr,
+    with bounds checking.
+
+    @param[in] ptr  JSON pointer to the desired element
+
+    @return reference to the element pointed to by @a ptr
+
+    @complexity Constant.
+
+    @throw std::out_of_range      if the JSON pointer can not be resolved
+    @throw std::domain_error      if an array index begins with '0'
+    @throw std::invalid_argument  if an array index was not a number
+
+    @liveexample{The behavior is shown in the example.,at_json_pointer}
+
+    @since version 2.0.0
+    */
+    reference at(const json_pointer& ptr)
+    {
+        return ptr.get_checked(this);
+    }
+
+    /*!
+    @brief access specified element via JSON Pointer
+
+    Returns a const reference to the element at with specified JSON pointer @a
+    ptr, with bounds checking.
+
+    @param[in] ptr  JSON pointer to the desired element
+
+    @return reference to the element pointed to by @a ptr
+
+    @complexity Constant.
+
+    @throw std::out_of_range      if the JSON pointer can not be resolved
+    @throw std::domain_error      if an array index begins with '0'
+    @throw std::invalid_argument  if an array index was not a number
+
+    @liveexample{The behavior is shown in the example.,at_json_pointer_const}
+
+    @since version 2.0.0
+    */
+    const_reference at(const json_pointer& ptr) const
+    {
+        return ptr.get_checked(this);
+    }
+
+    /*!
+    @brief return flattened JSON value
+
+    The function creates a JSON object whose keys are JSON pointers (see [RFC
+    6901](https://tools.ietf.org/html/rfc6901)) and whose values are all
+    primitive. The original JSON value can be restored using the @ref
+    unflatten() function.
+
+    @return an object that maps JSON pointers to primitve values
+
+    @note Empty objects and arrays are flattened to `null` and will not be
+          reconstructed correctly by the @ref unflatten() function.
+
+    @complexity Linear in the size the JSON value.
+
+    @liveexample{The following code shows how a JSON object is flattened to an
+    object whose keys consist of JSON pointers.,flatten}
+
+    @sa @ref unflatten() for the reverse function
+
+    @since version 2.0.0
+    */
+    basic_json flatten() const
+    {
+        basic_json result(value_t::object);
+        json_pointer::flatten("", *this, result);
+        return result;
+    }
+
+    /*!
+    @brief unflatten a previously flattened JSON value
+
+    The function restores the arbitrary nesting of a JSON value that has been
+    flattened before using the @ref flatten() function. The JSON value must
+    meet certain constraints:
+    1. The value must be an object.
+    2. The keys must be JSON pointers (see
+       [RFC 6901](https://tools.ietf.org/html/rfc6901))
+    3. The mapped values must be primitive JSON types.
+
+    @return the original JSON from a flattened version
+
+    @note Empty objects and arrays are flattened by @ref flatten() to `null`
+          values and can not unflattened to their original type. Apart from
+          this example, for a JSON value `j`, the following is always true:
+          `j == j.flatten().unflatten()`.
+
+    @complexity Linear in the size the JSON value.
+
+    @liveexample{The following code shows how a flattened JSON object is
+    unflattened into the original nested JSON object.,unflatten}
+
+    @sa @ref flatten() for the reverse function
+
+    @since version 2.0.0
+    */
+    basic_json unflatten() const
+    {
+        return json_pointer::unflatten(*this);
+    }
+
+    /// @}
+
+    //////////////////////////
+    // JSON Patch functions //
+    //////////////////////////
+
+    /// @name JSON Patch functions
+    /// @{
+
+    /*!
+    @brief applies a JSON patch
+
+    [JSON Patch](http://jsonpatch.com) defines a JSON document structure for
+    expressing a sequence of operations to apply to a JSON) document. With
+    this funcion, a JSON Patch is applied to the current JSON value by
+    executing all operations from the patch.
+
+    @param[in] json_patch  JSON patch document
+    @return patched document
+
+    @note The application of a patch is atomic: Either all operations succeed
+          and the patched document is returned or an exception is thrown. In
+          any case, the original value is not changed: the patch is applied
+          to a copy of the value.
+
+    @throw std::out_of_range if a JSON pointer inside the patch could not
+    be resolved successfully in the current JSON value; example: `"key baz
+    not found"`
+    @throw invalid_argument if the JSON patch is malformed (e.g., mandatory
+    attributes are missing); example: `"operation add must have member path"`
+
+    @complexity Linear in the size of the JSON value and the length of the
+    JSON patch. As usually only a fraction of the JSON value is affected by
+    the patch, the complexity can usually be neglected.
+
+    @liveexample{The following code shows how a JSON patch is applied to a
+    value.,patch}
+
+    @sa @ref diff -- create a JSON patch by comparing two JSON values
+
+    @sa [RFC 6902 (JSON Patch)](https://tools.ietf.org/html/rfc6902)
+    @sa [RFC 6901 (JSON Pointer)](https://tools.ietf.org/html/rfc6901)
+
+    @since version 2.0.0
+    */
+    basic_json patch(const basic_json& json_patch) const
+    {
+        // make a working copy to apply the patch to
+        basic_json result = *this;
+
+        // the valid JSON Patch operations
+        enum class patch_operations {add, remove, replace, move, copy, test, invalid};
+
+        const auto get_op = [](const std::string op)
+        {
+            if (op == "add")
+            {
+                return patch_operations::add;
+            }
+            if (op == "remove")
+            {
+                return patch_operations::remove;
+            }
+            if (op == "replace")
+            {
+                return patch_operations::replace;
+            }
+            if (op == "move")
+            {
+                return patch_operations::move;
+            }
+            if (op == "copy")
+            {
+                return patch_operations::copy;
+            }
+            if (op == "test")
+            {
+                return patch_operations::test;
+            }
+
+            return patch_operations::invalid;
+        };
+
+        // wrapper for "add" operation; add value at ptr
+        const auto operation_add = [&result](json_pointer & ptr, basic_json val)
+        {
+            // adding to the root of the target document means replacing it
+            if (ptr.is_root())
+            {
+                result = val;
+            }
+            else
+            {
+                // make sure the top element of the pointer exists
+                json_pointer top_pointer = ptr.top();
+                if (top_pointer != ptr)
+                {
+                    basic_json& x = result.at(top_pointer);
+                }
+
+                // get reference to parent of JSON pointer ptr
+                const auto last_path = ptr.pop_back();
+                basic_json& parent = result[ptr];
+
+                switch (parent.m_type)
+                {
+                    case value_t::null:
+                    case value_t::object:
+                    {
+                        // use operator[] to add value
+                        parent[last_path] = val;
+                        break;
+                    }
+
+                    case value_t::array:
+                    {
+                        if (last_path == "-")
+                        {
+                            // special case: append to back
+                            parent.push_back(val);
+                        }
+                        else
+                        {
+                            const auto idx = std::stoi(last_path);
+                            if (static_cast<size_type>(idx) > parent.size())
+                            {
+                                // avoid undefined behavior
+                                throw std::out_of_range("array index " + std::to_string(idx) + " is out of range");
+                            }
+                            else
+                            {
+                                // default case: insert add offset
+                                parent.insert(parent.begin() + static_cast<difference_type>(idx), val);
+                            }
+                        }
+                        break;
+                    }
+
+                    default:
+                    {
+                        // if there exists a parent it cannot be primitive
+                        assert(false);  // LCOV_EXCL_LINE
+                    }
+                }
+            }
+        };
+
+        // wrapper for "remove" operation; remove value at ptr
+        const auto operation_remove = [&result](json_pointer & ptr)
+        {
+            // get reference to parent of JSON pointer ptr
+            const auto last_path = ptr.pop_back();
+            basic_json& parent = result.at(ptr);
+
+            // remove child
+            if (parent.is_object())
+            {
+                // perform range check
+                auto it = parent.find(last_path);
+                if (it != parent.end())
+                {
+                    parent.erase(it);
+                }
+                else
+                {
+                    throw std::out_of_range("key '" + last_path + "' not found");
+                }
+            }
+            else if (parent.is_array())
+            {
+                // note erase performs range check
+                parent.erase(static_cast<size_type>(std::stoi(last_path)));
+            }
+        };
+
+        // type check
+        if (not json_patch.is_array())
+        {
+            // a JSON patch must be an array of objects
+            throw std::invalid_argument("JSON patch must be an array of objects");
+        }
+
+        // iterate and apply th eoperations
+        for (const auto& val : json_patch)
+        {
+            // 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&
+            {
+                // find value
+                auto it = val.m_value.object->find(member);
+
+                // context-sensitive error message
+                const auto error_msg = (op == "op") ? "operation" : "operation '" + op + "'";
+
+                // check if desired value is present
+                if (it == val.m_value.object->end())
+                {
+                    throw std::invalid_argument(error_msg + " must have member '" + member + "'");
+                }
+
+                // check if result is of type string
+                if (string_type and not it->second.is_string())
+                {
+                    throw std::invalid_argument(error_msg + " must have string member '" + member + "'");
+                }
+
+                // no error: return value
+                return it->second;
+            };
+
+            // type check
+            if (not val.is_object())
+            {
+                throw std::invalid_argument("JSON patch must be an array of objects");
+            }
+
+            // collect mandatory members
+            const std::string op = get_value("op", "op", true);
+            const std::string path = get_value(op, "path", true);
+            json_pointer ptr(path);
+
+            switch (get_op(op))
+            {
+                case patch_operations::add:
+                {
+                    operation_add(ptr, get_value("add", "value", false));
+                    break;
+                }
+
+                case patch_operations::remove:
+                {
+                    operation_remove(ptr);
+                    break;
+                }
+
+                case patch_operations::replace:
+                {
+                    // the "path" location must exist - use at()
+                    result.at(ptr) = get_value("replace", "value", false);
+                    break;
+                }
+
+                case patch_operations::move:
+                {
+                    const std::string from_path = get_value("move", "from", true);
+                    json_pointer from_ptr(from_path);
+
+                    // the "from" location must exist - use at()
+                    basic_json v = result.at(from_ptr);
+
+                    // The move operation is functionally identical to a
+                    // "remove" operation on the "from" location, followed
+                    // immediately by an "add" operation at the target
+                    // location with the value that was just removed.
+                    operation_remove(from_ptr);
+                    operation_add(ptr, v);
+                    break;
+                }
+
+                case patch_operations::copy:
+                {
+                    const std::string from_path = get_value("copy", "from", true);;
+                    const json_pointer from_ptr(from_path);
+
+                    // the "from" location must exist - use at()
+                    result[ptr] = result.at(from_ptr);
+                    break;
+                }
+
+                case patch_operations::test:
+                {
+                    bool success = false;
+                    try
+                    {
+                        // check if "value" matches the one at "path"
+                        // the "path" location must exist - use at()
+                        success = (result.at(ptr) == get_value("test", "value", false));
+                    }
+                    catch (std::out_of_range&)
+                    {
+                        // ignore out of range errors: success remains false
+                    }
+
+                    // throw an exception if test fails
+                    if (not success)
+                    {
+                        throw std::domain_error("unsuccessful: " + val.dump());
+                    }
+
+                    break;
+                }
+
+                case patch_operations::invalid:
+                {
+                    // op must be "add", "remove", "replace", "move", "copy", or
+                    // "test"
+                    throw std::invalid_argument("operation value '" + op + "' is invalid");
+                }
+            }
+        }
+
+        return result;
+    }
+
+    /*!
+    @brief creates a diff as a JSON patch
+
+    Creates a [JSON Patch](http://jsonpatch.com) so that value @a source can
+    be changed into the value @a target by calling @ref patch function.
+
+    @invariant For two JSON values @a source and @a target, the following code
+    yields always `true`:
+    @code {.cpp}
+    source.patch(diff(source, target)) == target;
+    @endcode
+
+    @note Currently, only `remove`, `add`, and `replace` operations are
+          generated.
+
+    @param[in] source  JSON value to copare from
+    @param[in] target  JSON value to copare against
+    @param[in] path    helper value to create JSON pointers
+
+    @return a JSON patch to convert the @a source to @a target
+
+    @complexity Linear in the lengths of @a source and @a target.
+
+    @liveexample{The following code shows how a JSON patch is created as a
+    diff for two JSON values.,diff}
+
+    @sa @ref patch -- apply a JSON patch
+
+    @sa [RFC 6902 (JSON Patch)](https://tools.ietf.org/html/rfc6902)
+
+    @since version 2.0.0
+    */
+    static basic_json diff(const basic_json& source,
+                           const basic_json& target,
+                           std::string path = "")
+    {
+        // the patch
+        basic_json result(value_t::array);
+
+        // if the values are the same, return empty patch
+        if (source == target)
+        {
+            return result;
+        }
+
+        if (source.type() != target.type())
+        {
+            // different types: replace value
+            result.push_back(
+            {
+                {"op", "replace"},
+                {"path", path},
+                {"value", target}
+            });
+        }
+        else
+        {
+            switch (source.type())
+            {
+                case value_t::array:
+                {
+                    // first pass: traverse common elements
+                    size_t i = 0;
+                    while (i < source.size() and i < target.size())
+                    {
+                        // recursive call to compare array values at index i
+                        auto temp_diff = diff(source[i], target[i], path + "/" + std::to_string(i));
+                        result.insert(result.end(), temp_diff.begin(), temp_diff.end());
+                        ++i;
+                    }
+
+                    // i now reached the end of at least one array
+                    // in a second pass, traverse the remaining elements
+
+                    // remove my remaining elements
+                    const auto end_index = static_cast<difference_type>(result.size());
+                    while (i < source.size())
+                    {
+                        // add operations in reverse order to avoid invalid
+                        // indices
+                        result.insert(result.begin() + end_index, object(
+                        {
+                            {"op", "remove"},
+                            {"path", path + "/" + std::to_string(i)}
+                        }));
+                        ++i;
+                    }
+
+                    // add other remaining elements
+                    while (i < target.size())
+                    {
+                        result.push_back(
+                        {
+                            {"op", "add"},
+                            {"path", path + "/" + std::to_string(i)},
+                            {"value", target[i]}
+                        });
+                        ++i;
+                    }
+
+                    break;
+                }
+
+                case value_t::object:
+                {
+                    // first pass: traverse this object's elements
+                    for (auto it = source.begin(); it != source.end(); ++it)
+                    {
+                        // escape the key name to be used in a JSON patch
+                        const auto key = json_pointer::escape(it.key());
+
+                        if (target.find(it.key()) != target.end())
+                        {
+                            // recursive call to compare object values at key it
+                            auto temp_diff = diff(it.value(), target[it.key()], path + "/" + key);
+                            result.insert(result.end(), temp_diff.begin(), temp_diff.end());
+                        }
+                        else
+                        {
+                            // found a key that is not in o -> remove it
+                            result.push_back(object(
+                            {
+                                {"op", "remove"},
+                                {"path", path + "/" + key}
+                            }));
+                        }
+                    }
+
+                    // second pass: traverse other object's elements
+                    for (auto it = target.begin(); it != target.end(); ++it)
+                    {
+                        if (source.find(it.key()) == source.end())
+                        {
+                            // found a key that is not in this -> add it
+                            const auto key = json_pointer::escape(it.key());
+                            result.push_back(
+                            {
+                                {"op", "add"},
+                                {"path", path + "/" + key},
+                                {"value", it.value()}
+                            });
+                        }
+                    }
+
+                    break;
+                }
+
+                default:
+                {
+                    // both primitive type: replace value
+                    result.push_back(
+                    {
+                        {"op", "replace"},
+                        {"path", path},
+                        {"value", target}
+                    });
+                    break;
+                }
+            }
+        }
+
+        return result;
+    }
+
+    /// @}
 };
 
 
@@ -8854,8 +9948,8 @@
 /*!
 @brief default JSON class
 
-This type is the default specialization of the @ref basic_json class which uses
-the standard template types.
+This type is the default specialization of the @ref basic_json class which
+uses the standard template types.
 
 @since version 1.0.0
 */
@@ -8863,9 +9957,9 @@
 }
 
 
-/////////////////////////
-// nonmember functions //
-/////////////////////////
+///////////////////////
+// nonmember support //
+///////////////////////
 
 // specialization of std::swap, and std::hash
 namespace std
@@ -8906,9 +10000,9 @@
 /*!
 @brief user-defined string literal for JSON values
 
-This operator implements a user-defined string literal for JSON objects. It can
-be used by adding \p "_json" to a string literal and returns a JSON object if
-no parse error occurred.
+This operator implements a user-defined string literal for JSON objects. It
+can be used by adding \p "_json" to a string literal and returns a JSON object
+if no parse error occurred.
 
 @param[in] s  a string representation of a JSON object
 @return a JSON object
@@ -8920,6 +10014,16 @@
     return nlohmann::json::parse(reinterpret_cast<const nlohmann::json::string_t::value_type*>(s));
 }
 
+/*!
+@brief user-defined string literal for JSON pointer
+
+@since version 2.0.0
+*/
+inline nlohmann::json::json_pointer operator "" _json_pointer(const char* s, std::size_t)
+{
+    return nlohmann::json::json_pointer(s);
+}
+
 // restore GCC/clang diagnostic settings
 #if defined(__clang__) || defined(__GNUC__) || defined(__GNUG__)
     #pragma GCC diagnostic pop
diff --git a/src/json.hpp.re2c b/src/json.hpp.re2c
index ebf83d8..aba10b4 100644
--- a/src/json.hpp.re2c
+++ b/src/json.hpp.re2c
@@ -198,6 +198,9 @@
           AllocatorType>;
 
   public:
+    // forward declarations
+    template<typename Base> class json_reverse_iterator;
+    class json_pointer;
 
     /////////////////////
     // container types //
@@ -227,9 +230,6 @@
     /// the type of an element const pointer
     using const_pointer = typename std::allocator_traits<allocator_type>::const_pointer;
 
-    // forward declaration
-    template<typename Base> class json_reverse_iterator;
-
     /// an iterator for a basic_json container
     class iterator;
     /// a const iterator for a basic_json container
@@ -271,8 +271,8 @@
 
     @tparam ObjectType  the container to store objects (e.g., `std::map` or
     `std::unordered_map`)
-    @tparam StringType the type of the keys or names (e.g., `std::string`). The
-    comparison function `std::less<StringType>` is used to order elements
+    @tparam StringType the type of the keys or names (e.g., `std::string`).
+    The comparison function `std::less<StringType>` is used to order elements
     inside the container.
     @tparam AllocatorType the allocator to use for objects (e.g.,
     `std::allocator`)
@@ -280,8 +280,8 @@
     #### Default type
 
     With the default values for @a ObjectType (`std::map`), @a StringType
-    (`std::string`), and @a AllocatorType (`std::allocator`), the default value
-    for @a object_t is:
+    (`std::string`), and @a AllocatorType (`std::allocator`), the default
+    value for @a object_t is:
 
     @code {.cpp}
     std::map<
@@ -298,16 +298,16 @@
     the default type, objects have the following behavior:
 
     - When all names are unique, objects will be interoperable in the sense
-      that all software implementations receiving that object will agree on the
-      name-value mappings.
+      that all software implementations receiving that object will agree on
+      the name-value mappings.
     - When the names within an object are not unique, later stored name/value
       pairs overwrite previously stored name/value pairs, leaving the used
       names unique. For instance, `{"key": 1}` and `{"key": 2, "key": 1}` will
       be treated as equal and both stored as `{"key": 1}`.
     - Internally, name/value pairs are stored in lexicographical order of the
-      names. Objects will also be serialized (see @ref dump) in this order. For
-      instance, `{"b": 1, "a": 2}` and `{"a": 2, "b": 1}` will be stored and
-      serialized as `{"a": 2, "b": 1}`.
+      names. Objects will also be serialized (see @ref dump) in this order.
+      For instance, `{"b": 1, "a": 2}` and `{"a": 2, "b": 1}` will be stored
+      and serialized as `{"a": 2, "b": 1}`.
     - When comparing objects, the order of the name/value pairs is irrelevant.
       This makes objects interoperable in the sense that they will not be
       affected by these differences. For instance, `{"b": 1, "a": 2}` and
@@ -320,23 +320,24 @@
 
     In this class, the object's limit of nesting is not constraint explicitly.
     However, a maximum depth of nesting may be introduced by the compiler or
-    runtime environment. A theoretical limit can be queried by calling the @ref
-    max_size function of a JSON object.
+    runtime environment. A theoretical limit can be queried by calling the
+    @ref max_size function of a JSON object.
 
     #### Storage
 
     Objects are stored as pointers in a @ref basic_json type. That is, for any
-    access to object values, a pointer of type `object_t*` must be dereferenced.
+    access to object values, a pointer of type `object_t*` must be
+    dereferenced.
 
     @sa @ref array_t -- type for an array value
 
     @since version 1.0.0
 
-    @note The order name/value pairs are added to the object is *not* preserved
-    by the library. Therefore, iterating an object may return name/value pairs
-    in a different order than they were originally stored. In fact, keys will
-    be traversed in alphabetical order as `std::map` with `std::less` is used
-    by default. Please note this behavior conforms to [RFC
+    @note The order name/value pairs are added to the object is *not*
+    preserved by the library. Therefore, iterating an object may return
+    name/value pairs in a different order than they were originally stored. In
+    fact, keys will be traversed in alphabetical order as `std::map` with
+    `std::less` is used by default. Please note this behavior conforms to [RFC
     7159](http://rfc7159.net/rfc7159), because any order implements the
     specified "unordered" nature of JSON objects.
     */
@@ -378,8 +379,8 @@
 
     In this class, the array's limit of nesting is not constraint explicitly.
     However, a maximum depth of nesting may be introduced by the compiler or
-    runtime environment. A theoretical limit can be queried by calling the @ref
-    max_size function of a JSON array.
+    runtime environment. A theoretical limit can be queried by calling the
+    @ref max_size function of a JSON array.
 
     #### Storage
 
@@ -399,8 +400,8 @@
     > A string is a sequence of zero or more Unicode characters.
 
     To store objects in C++, a type is defined by the template parameter
-    described below. Unicode values are split by the JSON class into byte-sized
-    characters during deserialization.
+    described below. Unicode values are split by the JSON class into
+    byte-sized characters during deserialization.
 
     @tparam StringType  the container to store strings (e.g., `std::string`).
     Note this container is used for keys/names in objects, see @ref object_t.
@@ -469,19 +470,19 @@
     @brief a type for a number (integer)
 
     [RFC 7159](http://rfc7159.net/rfc7159) describes numbers as follows:
-    > The representation of numbers is similar to that used in most programming
-    > languages. A number is represented in base 10 using decimal digits. It
-    > contains an integer component that may be prefixed with an optional minus
-    > sign, which may be followed by a fraction part and/or an exponent part.
-    > Leading zeros are not allowed. (...) Numeric values that cannot be
-    > represented in the grammar below (such as Infinity and NaN) are not
-    > permitted.
+    > The representation of numbers is similar to that used in most
+    > programming languages. A number is represented in base 10 using decimal
+    > digits. It contains an integer component that may be prefixed with an
+    > optional minus sign, which may be followed by a fraction part and/or an
+    > exponent part. Leading zeros are not allowed. (...) Numeric values that
+    > cannot be represented in the grammar below (such as Infinity and NaN)
+    > are not permitted.
 
-    This description includes both integer and floating-point numbers. However,
-    C++ allows more precise storage if it is known whether the number is a
-    signed integer, an unsigned integer or a floating-point number. Therefore,
-    three different types, @ref number_integer_t, @ref number_unsigned_t and
-    @ref number_float_t are used.
+    This description includes both integer and floating-point numbers.
+    However, C++ allows more precise storage if it is known whether the number
+    is a signed integer, an unsigned integer or a floating-point number.
+    Therefore, three different types, @ref number_integer_t, @ref
+    number_unsigned_t and @ref number_float_t are used.
 
     To store integer numbers in C++, a type is defined by the template
     parameter @a NumberIntegerType which chooses the type to use.
@@ -500,8 +501,8 @@
     - The restrictions about leading zeros is not enforced in C++. Instead,
       leading zeros in integer literals lead to an interpretation as octal
       number. Internally, the value will be stored as decimal number. For
-      instance, the C++ integer literal `010` will be serialized to `8`. During
-      deserialization, leading zeros yield an error.
+      instance, the C++ integer literal `010` will be serialized to `8`.
+      During deserialization, leading zeros yield an error.
     - Not-a-number (NaN) values will be serialized to `null`.
 
     #### Limits
@@ -512,9 +513,10 @@
     When the default type is used, the maximal integer number that can be
     stored is `9223372036854775807` (INT64_MAX) and the minimal integer number
     that can be stored is `-9223372036854775808` (INT64_MIN). Integer numbers
-    that are out of range will yield over/underflow when used in a constructor.
-    During deserialization, too large or small integer numbers will be
-    automatically be stored as @ref number_unsigned_t or @ref number_float_t.
+    that are out of range will yield over/underflow when used in a
+    constructor. During deserialization, too large or small integer numbers
+    will be automatically be stored as @ref number_unsigned_t or @ref
+    number_float_t.
 
     [RFC 7159](http://rfc7159.net/rfc7159) further states:
     > Note that when such software is used, numbers that are integers and are
@@ -540,27 +542,27 @@
     @brief a type for a number (unsigned)
 
     [RFC 7159](http://rfc7159.net/rfc7159) describes numbers as follows:
-    > The representation of numbers is similar to that used in most programming
-    > languages. A number is represented in base 10 using decimal digits. It
-    > contains an integer component that may be prefixed with an optional minus
-    > sign, which may be followed by a fraction part and/or an exponent part.
-    > Leading zeros are not allowed. (...) Numeric values that cannot be
-    > represented in the grammar below (such as Infinity and NaN) are not
-    > permitted.
+    > The representation of numbers is similar to that used in most
+    > programming languages. A number is represented in base 10 using decimal
+    > digits. It contains an integer component that may be prefixed with an
+    > optional minus sign, which may be followed by a fraction part and/or an
+    > exponent part. Leading zeros are not allowed. (...) Numeric values that
+    > cannot be represented in the grammar below (such as Infinity and NaN)
+    > are not permitted.
 
-    This description includes both integer and floating-point numbers. However,
-    C++ allows more precise storage if it is known whether the number is a
-    signed integer, an unsigned integer or a floating-point number. Therefore,
-    three different types, @ref number_integer_t, @ref number_unsigned_t and
-    @ref number_float_t are used.
+    This description includes both integer and floating-point numbers.
+    However, C++ allows more precise storage if it is known whether the number
+    is a signed integer, an unsigned integer or a floating-point number.
+    Therefore, three different types, @ref number_integer_t, @ref
+    number_unsigned_t and @ref number_float_t are used.
 
-    To store unsigned integer numbers in C++, a type is defined by the template
-    parameter @a NumberUnsignedType which chooses the type to use.
+    To store unsigned integer numbers in C++, a type is defined by the
+    template parameter @a NumberUnsignedType which chooses the type to use.
 
     #### Default type
 
-    With the default values for @a NumberUnsignedType (`uint64_t`), the default
-    value for @a number_unsigned_t is:
+    With the default values for @a NumberUnsignedType (`uint64_t`), the
+    default value for @a number_unsigned_t is:
 
     @code {.cpp}
     uint64_t
@@ -571,8 +573,8 @@
     - The restrictions about leading zeros is not enforced in C++. Instead,
       leading zeros in integer literals lead to an interpretation as octal
       number. Internally, the value will be stored as decimal number. For
-      instance, the C++ integer literal `010` will be serialized to `8`. During
-      deserialization, leading zeros yield an error.
+      instance, the C++ integer literal `010` will be serialized to `8`.
+      During deserialization, leading zeros yield an error.
     - Not-a-number (NaN) values will be serialized to `null`.
 
     #### Limits
@@ -612,19 +614,19 @@
     @brief a type for a number (floating-point)
 
     [RFC 7159](http://rfc7159.net/rfc7159) describes numbers as follows:
-    > The representation of numbers is similar to that used in most programming
-    > languages. A number is represented in base 10 using decimal digits. It
-    > contains an integer component that may be prefixed with an optional minus
-    > sign, which may be followed by a fraction part and/or an exponent part.
-    > Leading zeros are not allowed. (...) Numeric values that cannot be
-    > represented in the grammar below (such as Infinity and NaN) are not
-    > permitted.
+    > The representation of numbers is similar to that used in most
+    > programming languages. A number is represented in base 10 using decimal
+    > digits. It contains an integer component that may be prefixed with an
+    > optional minus sign, which may be followed by a fraction part and/or an
+    > exponent part. Leading zeros are not allowed. (...) Numeric values that
+    > cannot be represented in the grammar below (such as Infinity and NaN)
+    > are not permitted.
 
-    This description includes both integer and floating-point numbers. However,
-    C++ allows more precise storage if it is known whether the number is a
-    signed integer, an unsigned integer or a floating-point number. Therefore,
-    three different types, @ref number_integer_t, @ref number_unsigned_t and
-    @ref number_float_t are used.
+    This description includes both integer and floating-point numbers.
+    However, C++ allows more precise storage if it is known whether the number
+    is a signed integer, an unsigned integer or a floating-point number.
+    Therefore, three different types, @ref number_integer_t, @ref
+    number_unsigned_t and @ref number_float_t are used.
 
     To store floating-point numbers in C++, a type is defined by the template
     parameter @a NumberFloatType which chooses the type to use.
@@ -641,8 +643,8 @@
     #### Default behavior
 
     - The restrictions about leading zeros is not enforced in C++. Instead,
-      leading zeros in floating-point literals will be ignored. Internally, the
-      value will be stored as decimal number. For instance, the C++
+      leading zeros in floating-point literals will be ignored. Internally,
+      the value will be stored as decimal number. For instance, the C++
       floating-point literal `01.2` will be serialized to `1.2`. During
       deserialization, leading zeros yield an error.
     - Not-a-number (NaN) values will be serialized to `null`.
@@ -653,9 +655,9 @@
     > This specification allows implementations to set limits on the range and
     > precision of numbers accepted. Since software that implements IEEE
     > 754-2008 binary64 (double precision) numbers is generally available and
-    > widely used, good interoperability can be achieved by implementations that
-    > expect no more precision or range than these provide, in the sense that
-    > implementations will approximate JSON numbers within the expected
+    > widely used, good interoperability can be achieved by implementations
+    > that expect no more precision or range than these provide, in the sense
+    > that implementations will approximate JSON numbers within the expected
     > precision.
 
     This implementation does exactly follow this approach, as it uses double
@@ -709,73 +711,6 @@
 
   private:
 
-    /*!
-    @brief a type to hold JSON type information
-
-    This bitfield type holds information about JSON types. It is internally
-    used to hold the basic JSON type enumeration, as well as additional
-    information in the case of values that have been parsed from a string
-    including whether of not it was created directly or parsed, and in the
-    case of floating point numbers the number of significant figures in the
-    original representaiton and if it was in exponential form, if a '+' was
-    included in the exponent and the capitilization of the exponent marker.
-    The sole purpose of this information is to permit accurate round trips.
-
-    @since version 2.0.0
-    */
-    union type_data_t
-    {
-        struct
-        {
-            /// the type of the value (@ref value_t)
-            uint16_t type : 4;
-            /// whether the number was parsed from a string
-            uint16_t parsed : 1;
-            /// whether parsed number contained an exponent ('e'/'E')
-            uint16_t has_exp : 1;
-            /// whether parsed number contained a plus in the exponent
-            uint16_t exp_plus : 1;
-            /// whether parsed number's exponent was capitalized ('E')
-            uint16_t exp_cap : 1;
-            /// the number of figures for a parsed number
-            uint16_t precision : 8;
-        } bits;
-        uint16_t data;
-
-        /// return the type as value_t
-        operator value_t() const
-        {
-            return static_cast<value_t>(bits.type);
-        }
-
-        /// test type for equality (ignore other fields)
-        bool operator==(const value_t& rhs) const
-        {
-            return static_cast<value_t>(bits.type) == rhs;
-        }
-
-        /// assignment
-        type_data_t& operator=(value_t rhs)
-        {
-            bits.type = static_cast<uint16_t>(rhs);
-            return *this;
-        }
-
-        /// construct from value_t
-        type_data_t(value_t t) noexcept
-        {
-            *reinterpret_cast<uint16_t*>(this) = 0;
-            bits.type = static_cast<uint16_t>(t);
-        }
-
-        /// default constructor
-        type_data_t() noexcept
-        {
-            data = 0;
-            bits.type = reinterpret_cast<uint16_t>(value_t::null);
-        }
-    };
-
     /// helper for exception-safe object creation
     template<typename T, typename... Args>
     static T* create(Args&& ... args)
@@ -938,9 +873,9 @@
     influenced. When passed to @ref parse(std::istream&, parser_callback_t) or
     @ref parse(const string_t&, parser_callback_t), it is called on certain
     events (passed as @ref parse_event_t via parameter @a event) with a set
-    recursion depth @a depth and context JSON value @a parsed. The return value
-    of the callback function is a boolean indicating whether the element that
-    emitted the callback shall be kept or not.
+    recursion depth @a depth and context JSON value @a parsed. The return
+    value of the callback function is a boolean indicating whether the element
+    that emitted the callback shall be kept or not.
 
     We distinguish six scenarios (determined by the event type) in which the
     callback function can be called. The following table describes the values
@@ -960,8 +895,8 @@
 
     - Discarded values in structured types are skipped. That is, the parser
       will behave as if the discarded value was never read.
-    - In case a value outside a structured type is skipped, it is replaced with
-      `null`. This case happens if the top-level element is skipped.
+    - In case a value outside a structured type is skipped, it is replaced
+      with `null`. This case happens if the top-level element is skipped.
 
     @param[in] depth  the depth of the recursion during parsing
 
@@ -1065,8 +1000,8 @@
     Create a `null` JSON value. This is the explicitly version of the `null`
     value constructor as it takes a null pointer as parameter. It allows to
     create `null` values by explicitly assigning a `nullptr` to a JSON value.
-    The passed null pointer itself is not read -- it is only used to choose the
-    right constructor.
+    The passed null pointer itself is not read -- it is only used to choose
+    the right constructor.
 
     @complexity Constant.
 
@@ -1096,8 +1031,8 @@
 
     @throw std::bad_alloc if allocation for object value fails
 
-    @liveexample{The following code shows the constructor with an @ref object_t
-    parameter.,basic_json__object_t}
+    @liveexample{The following code shows the constructor with an @ref
+    object_t parameter.,basic_json__object_t}
 
     @sa @ref basic_json(const CompatibleObjectType&) -- create an object value
     from a compatible STL container
@@ -1177,11 +1112,11 @@
     any type @a CompatibleArrayType that can be used to construct values of
     type @ref array_t.
 
-    @tparam CompatibleArrayType An object type whose `value_type` is compatible
-    to @ref array_t. Examples include `std::vector`, `std::deque`, `std::list`,
-    `std::forward_list`, `std::array`, `std::set`, `std::unordered_set`,
-    `std::multiset`, and `unordered_multiset` with a `value_type` from which a
-    @ref basic_json value can be constructed.
+    @tparam CompatibleArrayType An object type whose `value_type` is
+    compatible to @ref array_t. Examples include `std::vector`, `std::deque`,
+    `std::list`, `std::forward_list`, `std::array`, `std::set`,
+    `std::unordered_set`, `std::multiset`, and `unordered_multiset` with a
+    `value_type` from which a @ref basic_json value can be constructed.
 
     @param[in] val  a value for the array
 
@@ -1225,8 +1160,8 @@
 
     @throw std::bad_alloc if allocation for string value fails
 
-    @liveexample{The following code shows the constructor with an @ref string_t
-    parameter.,basic_json__string_t}
+    @liveexample{The following code shows the constructor with an @ref
+    string_t parameter.,basic_json__string_t}
 
     @sa @ref basic_json(const typename string_t::value_type*) -- create a
     string value from a character pointer
@@ -1318,9 +1253,9 @@
     Create an integer number JSON value with a given content.
 
     @tparam T A helper type to remove this function via SFINAE in case @ref
-    number_integer_t is the same as `int`. In this case, this constructor would
-    have the same signature as @ref basic_json(const int value). Note the
-    helper type @a T is not visible in this constructor's interface.
+    number_integer_t is the same as `int`. In this case, this constructor
+    would have the same signature as @ref basic_json(const int value). Note
+    the helper type @a T is not visible in this constructor's interface.
 
     @param[in] val  an integer to create a JSON number from
 
@@ -1355,8 +1290,8 @@
     @note This constructor allows to pass enums directly to a constructor. As
     C++ has no way of specifying the type of an anonymous enum explicitly, we
     can only rely on the fact that such values implicitly convert to int. As
-    int may already be the same type of number_integer_t, we may need to switch
-    off the constructor @ref basic_json(const number_integer_t).
+    int may already be the same type of number_integer_t, we may need to
+    switch off the constructor @ref basic_json(const number_integer_t).
 
     @complexity Constant.
 
@@ -1383,8 +1318,8 @@
     construct values of type @ref number_integer_t.
 
     @tparam CompatibleNumberIntegerType An integer type which is compatible to
-    @ref number_integer_t. Examples include the types `int`, `int32_t`, `long`,
-    and `short`.
+    @ref number_integer_t. Examples include the types `int`, `int32_t`,
+    `long`, and `short`.
 
     @param[in] val  an integer to create a JSON number from
 
@@ -1442,12 +1377,12 @@
     /*!
     @brief create an unsigned number (implicit)
 
-    Create an unsigned number JSON value with a given content. This constructor
-    allows any type @a CompatibleNumberUnsignedType that can be used to
-    construct values of type @ref number_unsigned_t.
+    Create an unsigned number JSON value with a given content. This
+    constructor allows any type @a CompatibleNumberUnsignedType that can be
+    used to construct values of type @ref number_unsigned_t.
 
-    @tparam CompatibleNumberUnsignedType An integer type which is compatible to
-    @ref number_unsigned_t. Examples may include the types `unsigned int`,
+    @tparam CompatibleNumberUnsignedType An integer type which is compatible
+    to @ref number_unsigned_t. Examples may include the types `unsigned int`,
     `uint32_t`, or `unsigned short`.
 
     @param[in] val  an unsigned integer to create a JSON number from
@@ -1459,13 +1394,13 @@
 
     @since version 2.0.0
     */
-    template < typename CompatibleNumberUnsignedType, typename
-               std::enable_if <
-                   std::is_constructible<number_unsigned_t, CompatibleNumberUnsignedType>::value and
-                   std::numeric_limits<CompatibleNumberUnsignedType>::is_integer and
-                   !std::numeric_limits<CompatibleNumberUnsignedType>::is_signed,
-                   CompatibleNumberUnsignedType >::type
-               = 0 >
+    template <typename CompatibleNumberUnsignedType, typename
+              std::enable_if <
+                  std::is_constructible<number_unsigned_t, CompatibleNumberUnsignedType>::value and
+                  std::numeric_limits<CompatibleNumberUnsignedType>::is_integer and
+                  not std::numeric_limits<CompatibleNumberUnsignedType>::is_signed,
+                  CompatibleNumberUnsignedType>::type
+              = 0>
     basic_json(const CompatibleNumberUnsignedType val) noexcept
         : m_type(value_t::number_unsigned),
           m_value(static_cast<number_unsigned_t>(val))
@@ -1480,8 +1415,8 @@
 
     @note [RFC 7159](http://www.rfc-editor.org/rfc/rfc7159.txt), section 6
     disallows NaN values:
-    > Numeric values that cannot be represented in the grammar below (such
-    > as Infinity and NaN) are not permitted.
+    > Numeric values that cannot be represented in the grammar below (such as
+    > Infinity and NaN) are not permitted.
     In case the parameter @a val is not a number, a JSON null value is
     created instead.
 
@@ -1513,15 +1448,16 @@
     constructor allows any type @a CompatibleNumberFloatType that can be used
     to construct values of type @ref number_float_t.
 
-    @tparam CompatibleNumberFloatType A floating-point type which is compatible
-    to @ref number_float_t. Examples may include the types `float` or `double`.
+    @tparam CompatibleNumberFloatType A floating-point type which is
+    compatible to @ref number_float_t. Examples may include the types `float`
+    or `double`.
 
     @param[in] val  a floating-point to create a JSON number from
 
     @note [RFC 7159](http://www.rfc-editor.org/rfc/rfc7159.txt), section 6
     disallows NaN values:
-    > Numeric values that cannot be represented in the grammar below (such
-    > as Infinity and NaN) are not permitted.
+    > Numeric values that cannot be represented in the grammar below (such as
+    > Infinity and NaN) are not permitted.
     In case the parameter @a val is not a number, a JSON null value is
     created instead.
 
@@ -1592,13 +1528,13 @@
     used by the functions @ref array(std::initializer_list<basic_json>) and
     @ref object(std::initializer_list<basic_json>).
 
-    @param[in] manual_type internal parameter; when @a type_deduction is set to
-    `false`, the created JSON value will use the provided type (only @ref
+    @param[in] manual_type internal parameter; when @a type_deduction is set
+    to `false`, the created JSON value will use the provided type (only @ref
     value_t::array and @ref value_t::object are valid); when @a type_deduction
     is set to `true`, this parameter has no effect
 
-    @throw std::domain_error if @a type_deduction is `false`, @a manual_type is
-    `value_t::object`, but @a init contains an element which is not a pair
+    @throw std::domain_error if @a type_deduction is `false`, @a manual_type
+    is `value_t::object`, but @a init contains an element which is not a pair
     whose first element is a string; example: `"cannot create object from
     initializer list"`
 
@@ -1679,8 +1615,8 @@
     list of values `a, b, c`, creates the JSON value `[a, b, c]`. If the
     initializer list is empty, the empty array `[]` is created.
 
-    @note This function is only needed to express two edge cases that cannot be
-    realized with the initializer list constructor (@ref
+    @note This function is only needed to express two edge cases that cannot
+    be realized with the initializer list constructor (@ref
     basic_json(std::initializer_list<basic_json>, bool, value_t)). These cases
     are:
     1. creating an array whose elements are all pairs whose first element is a
@@ -1723,8 +1659,8 @@
     related function @ref array(std::initializer_list<basic_json>), there are
     no cases which can only be expressed by this function. That is, any
     initializer list @a init can also be passed to the initializer list
-    constructor
-    @ref basic_json(std::initializer_list<basic_json>, bool, value_t).
+    constructor @ref basic_json(std::initializer_list<basic_json>, bool,
+    value_t).
 
     @param[in] init  initializer list to create an object from (optional)
 
@@ -1755,8 +1691,8 @@
     /*!
     @brief construct an array with count copies of given value
 
-    Constructs a JSON array value by creating @a cnt copies of a passed
-    value. In case @a cnt is `0`, an empty array is created. As postcondition,
+    Constructs a JSON array value by creating @a cnt copies of a passed value.
+    In case @a cnt is `0`, an empty array is created. As postcondition,
     `std::distance(begin(),end()) == cnt` holds.
 
     @param[in] cnt  the number of JSON copies of @a val to create
@@ -1784,8 +1720,8 @@
     - In case of primitive types (number, boolean, or string), @a first must
       be `begin()` and @a last must be `end()`. In this case, the value is
       copied. Otherwise, std::out_of_range is thrown.
-    - In case of structured types (array, object), the constructor behaves
-      as similar versions for `std::vector`.
+    - In case of structured types (array, object), the constructor behaves as
+      similar versions for `std::vector`.
     - In case of a null type, std::domain_error is thrown.
 
     @tparam InputIT an input iterator type (@ref iterator or @ref
@@ -1800,8 +1736,8 @@
     boolean, or string) where an out of range error can be detected easily;
     example: `"iterators out of range"`
     @throw std::bad_alloc if allocation for object, array, or string fails
-    @throw std::domain_error if called with a null value; example: `"cannot use
-    construct with iterators from null"`
+    @throw std::domain_error if called with a null value; example: `"cannot
+    use construct with iterators from null"`
 
     @complexity Linear in distance between @a first and @a last.
 
@@ -2042,8 +1978,8 @@
     @brief copy assignment
 
     Copy assignment operator. Copies a JSON value via the "copy and swap"
-    strategy: It is expressed in terms of the copy constructor, destructor, and
-    the swap() member function.
+    strategy: It is expressed in terms of the copy constructor, destructor,
+    and the swap() member function.
 
     @param[in] other  value to copy from
 
@@ -2143,8 +2079,8 @@
     parameter.
 
     @param[in] indent if indent is nonnegative, then array elements and object
-    members will be pretty-printed with that indent level. An indent level of 0
-    will only insert newlines. -1 (the default) selects the most compact
+    members will be pretty-printed with that indent level. An indent level of
+    0 will only insert newlines. -1 (the default) selects the most compact
     representation
 
     @return string containing the serialization of the JSON value
@@ -2976,8 +2912,8 @@
     /*!
     @brief get a reference value (implicit)
 
-    Implict reference access to the internally stored JSON value. No copies are
-    made.
+    Implict reference access to the internally stored JSON value. No copies
+    are made.
 
     @warning Writing data to the referee of the result yields an undefined
     state.
@@ -3027,14 +2963,14 @@
     /*!
     @brief get a value (implicit)
 
-    Implicit type conversion between the JSON value and a compatible value. The
-    call is realized by calling @ref get() const.
+    Implicit type conversion between the JSON value and a compatible value.
+    The call is realized by calling @ref get() const.
 
     @tparam ValueType non-pointer type compatible to the JSON value, for
     instance `int` for JSON integer numbers, `bool` for JSON booleans, or
-    `std::vector` types for JSON arrays. The character type of @ref string_t as
-    well as an initializer list of this type is excluded to avoid ambiguities
-    as these types implicitly convert to `std::string`.
+    `std::vector` types for JSON arrays. The character type of @ref string_t
+    as well as an initializer list of this type is excluded to avoid
+    ambiguities as these types implicitly convert to `std::string`.
 
     @return copy of the JSON value, converted to type @a ValueType
 
@@ -3123,8 +3059,8 @@
     /*!
     @brief access specified array element with bounds checking
 
-    Returns a const reference to the element at specified location @a idx, with
-    bounds checking.
+    Returns a const reference to the element at specified location @a idx,
+    with bounds checking.
 
     @param[in] idx  index of the element to access
 
@@ -3215,8 +3151,8 @@
     /*!
     @brief access specified object element with bounds checking
 
-    Returns a const reference to the element at with specified key @a key, with
-    bounds checking.
+    Returns a const reference to the element at with specified key @a key,
+    with bounds checking.
 
     @param[in] key  key of the element to access
 
@@ -3273,8 +3209,8 @@
 
     @return reference to the element at index @a idx
 
-    @throw std::domain_error if JSON is not an array or null; example: `"cannot
-    use operator[] with string"`
+    @throw std::domain_error if JSON is not an array or null; example:
+    `"cannot use operator[] with string"`
 
     @complexity Constant if @a idx is in the range of the array. Otherwise
     linear in `idx - size()`.
@@ -3690,8 +3626,8 @@
 
     @complexity Constant.
 
-    @pre The JSON value must not be `null` (would throw `std::out_of_range`) or
-    an empty array or object (undefined behavior, guarded by assertions).
+    @pre The JSON value must not be `null` (would throw `std::out_of_range`)
+    or an empty array or object (undefined behavior, guarded by assertions).
     @post The JSON value remains unchanged.
 
     @throw std::out_of_range when called on `null` value
@@ -3732,8 +3668,8 @@
 
     @complexity Constant.
 
-    @pre The JSON value must not be `null` (would throw `std::out_of_range`) or
-    an empty array or object (undefined behavior, guarded by assertions).
+    @pre The JSON value must not be `null` (would throw `std::out_of_range`)
+    or an empty array or object (undefined behavior, guarded by assertions).
     @post The JSON value remains unchanged.
 
     @throw std::out_of_range when called on `null` value.
@@ -3772,16 +3708,16 @@
     will be `null`.
 
     @param[in] pos iterator to the element to remove
-    @return Iterator following the last removed element. If the iterator @a pos
-    refers to the last element, the `end()` iterator is returned.
+    @return Iterator following the last removed element. If the iterator @a
+    pos refers to the last element, the `end()` iterator is returned.
 
     @tparam InteratorType an @ref iterator or @ref const_iterator
 
     @post Invalidates iterators and references at or after the point of the
     erase, including the `end()` iterator.
 
-    @throw std::domain_error if called on a `null` value; example: `"cannot use
-    erase() with null"`
+    @throw std::domain_error if called on a `null` value; example: `"cannot
+    use erase() with null"`
     @throw std::domain_error if called on an iterator which does not belong to
     the current JSON value; example: `"iterator does not fit current value"`
     @throw std::out_of_range if called on a primitive type with invalid
@@ -3797,12 +3733,12 @@
     @liveexample{The example shows the result of `erase()` for different JSON
     types.,erase__IteratorType}
 
-    @sa @ref erase(InteratorType, InteratorType) -- removes the elements in the
-    given range
+    @sa @ref erase(InteratorType, InteratorType) -- removes the elements in
+    the given range
     @sa @ref erase(const typename object_t::key_type&) -- removes the element
     from an object at the given key
-    @sa @ref erase(const size_type) -- removes the element from an array at the
-    given index
+    @sa @ref erase(const size_type) -- removes the element from an array at
+    the given index
 
     @since version 1.0.0
     */
@@ -3871,9 +3807,9 @@
     /*!
     @brief remove elements given an iterator range
 
-    Removes the element specified by the range `[first; last)`. The iterator @a
-    first does not need to be dereferenceable if `first == last`: erasing an
-    empty range is a no-op.
+    Removes the element specified by the range `[first; last)`. The iterator
+    @a first does not need to be dereferenceable if `first == last`: erasing
+    an empty range is a no-op.
 
     If called on a primitive type other than `null`, the resulting JSON value
     will be `null`.
@@ -3888,8 +3824,8 @@
     @post Invalidates iterators and references at or after the point of the
     erase, including the `end()` iterator.
 
-    @throw std::domain_error if called on a `null` value; example: `"cannot use
-    erase() with null"`
+    @throw std::domain_error if called on a `null` value; example: `"cannot
+    use erase() with null"`
     @throw std::domain_error if called on iterators which does not belong to
     the current JSON value; example: `"iterators do not fit current value"`
     @throw std::out_of_range if called on a primitive type with invalid
@@ -3909,8 +3845,8 @@
     @sa @ref erase(InteratorType) -- removes the element at a given position
     @sa @ref erase(const typename object_t::key_type&) -- removes the element
     from an object at the given key
-    @sa @ref erase(const size_type) -- removes the element from an array at the
-    given index
+    @sa @ref erase(const size_type) -- removes the element from an array at
+    the given index
 
     @since version 1.0.0
     */
@@ -3986,8 +3922,8 @@
     @param[in] key value of the elements to remove
 
     @return Number of elements removed. If @a ObjectType is the default
-    `std::map` type, the return value will always be `0` (@a key was not found)
-    or `1` (@a key was found).
+    `std::map` type, the return value will always be `0` (@a key was not
+    found) or `1` (@a key was found).
 
     @post References and iterators to the erased elements are invalidated.
     Other references and iterators are not affected.
@@ -4000,10 +3936,10 @@
     @liveexample{The example shows the effect of `erase()`.,erase__key_type}
 
     @sa @ref erase(InteratorType) -- removes the element at a given position
-    @sa @ref erase(InteratorType, InteratorType) -- removes the elements in the
-    given range
-    @sa @ref erase(const size_type) -- removes the element from an array at the
-    given index
+    @sa @ref erase(InteratorType, InteratorType) -- removes the elements in
+    the given range
+    @sa @ref erase(const size_type) -- removes the element from an array at
+    the given index
 
     @since version 1.0.0
     */
@@ -4030,16 +3966,16 @@
 
     @throw std::domain_error when called on a type other than JSON array;
     example: `"cannot use erase() with null"`
-    @throw std::out_of_range when `idx >= size()`; example: `"index out of
-    range"`
+    @throw std::out_of_range when `idx >= size()`; example: `"array index 17
+    is out of range"`
 
     @complexity Linear in distance between @a idx and the end of the container.
 
     @liveexample{The example shows the effect of `erase()`.,erase__size_type}
 
     @sa @ref erase(InteratorType) -- removes the element at a given position
-    @sa @ref erase(InteratorType, InteratorType) -- removes the elements in the
-    given range
+    @sa @ref erase(InteratorType, InteratorType) -- removes the elements in
+    the given range
     @sa @ref erase(const typename object_t::key_type&) -- removes the element
     from an object at the given key
 
@@ -4052,7 +3988,7 @@
         {
             if (idx >= size())
             {
-                throw std::out_of_range("index out of range");
+                throw std::out_of_range("array index " + std::to_string(idx) + " is out of range");
             }
 
             assert(m_value.array != nullptr);
@@ -4078,7 +4014,8 @@
     @brief find an element in a JSON object
 
     Finds an element in a JSON object with key equivalent to @a key. If the
-    element is not found or the JSON value is not an object, end() is returned.
+    element is not found or the JSON value is not an object, end() is
+    returned.
 
     @param[in] key key value of the element to search for
 
@@ -4438,9 +4375,9 @@
     @brief wrapper to access iterator member functions in range-based for
 
     This function allows to access @ref iterator::key() and @ref
-    iterator::value() during range-based for loops. In these loops, a reference
-    to the JSON values is returned, so there is no access to the underlying
-    iterator.
+    iterator::value() during range-based for loops. In these loops, a
+    reference to the JSON values is returned, so there is no access to the
+    underlying iterator.
 
     @note The name of this function is not yet final and may change in the
     future.
@@ -4484,8 +4421,8 @@
             object      | result of function `object_t::empty()`
             array       | result of function `array_t::empty()`
 
-    @complexity Constant, as long as @ref array_t and @ref object_t satisfy the
-    Container concept; that is, their `empty()` functions have constant
+    @complexity Constant, as long as @ref array_t and @ref object_t satisfy
+    the Container concept; that is, their `empty()` functions have constant
     complexity.
 
     @requirement This function helps `basic_json` satisfying the
@@ -4547,8 +4484,9 @@
             object      | result of function object_t::size()
             array       | result of function array_t::size()
 
-    @complexity Constant, as long as @ref array_t and @ref object_t satisfy the
-    Container concept; that is, their size() functions have constant complexity.
+    @complexity Constant, as long as @ref array_t and @ref object_t satisfy
+    the Container concept; that is, their size() functions have constant
+    complexity.
 
     @requirement This function helps `basic_json` satisfying the
     [Container](http://en.cppreference.com/w/cpp/concept/Container)
@@ -4612,8 +4550,8 @@
             object      | result of function `object_t::max_size()`
             array       | result of function `array_t::max_size()`
 
-    @complexity Constant, as long as @ref array_t and @ref object_t satisfy the
-    Container concept; that is, their `max_size()` functions have constant
+    @complexity Constant, as long as @ref array_t and @ref object_t satisfy
+    the Container concept; that is, their `max_size()` functions have constant
     complexity.
 
     @requirement This function helps `basic_json` satisfying the
@@ -4835,8 +4773,8 @@
     @brief add an object to an object
 
     Inserts the given element @a val to the JSON object. If the function is
-    called on a JSON null value, an empty object is created before inserting @a
-    val.
+    called on a JSON null value, an empty object is created before inserting
+    @a val.
 
     @param[in] val the value to add to the JSON object
 
@@ -4878,7 +4816,55 @@
     reference operator+=(const typename object_t::value_type& val)
     {
         push_back(val);
-        return operator[](val.first);
+        return *this;
+    }
+
+    /*!
+    @brief add an object to an object
+
+    This function allows to use `push_back` with an initializer list. In case
+
+    1. the current value is an object,
+    2. the initializer list @a init contains only two elements, and
+    3. the first element of @a init is a string,
+
+    @a init is converted into an object element and added using
+    @ref push_back(const typename object_t::value_type&). Otherwise, @a init
+    is converted to a JSON value and added using @ref push_back(basic_json&&).
+
+    @param init  an initializer list
+
+    @complexity Linear in the size of the initializer list @a init.
+
+    @note This function is required to resolve an ambiguous overload error,
+          because pairs like `{"key", "value"}` can be both interpreted as
+          `object_t::value_type` or `std::initializer_list<basic_json>`, see
+          https://github.com/nlohmann/json/issues/235 for more information.
+
+    @liveexample{The example shows how initializer lists are treated as
+    objects when possible.,push_back__initializer_list}
+    */
+    void push_back(std::initializer_list<basic_json> init)
+    {
+        if (is_object() and init.size() == 2 and init.begin()->is_string())
+        {
+            const string_t key = *init.begin();
+            push_back(typename object_t::value_type(key, *(init.begin() + 1)));
+        }
+        else
+        {
+            push_back(basic_json(init));
+        }
+    }
+
+    /*!
+    @brief add an object to an object
+    @copydoc push_back(std::initializer_list<basic_json>)
+    */
+    reference operator+=(std::initializer_list<basic_json> init)
+    {
+        push_back(init);
+        return *this;
     }
 
     /*!
@@ -5026,6 +5012,7 @@
             throw std::domain_error("iterator does not fit current value");
         }
 
+        // check if range iterators belong to the same JSON object
         if (first.m_object != last.m_object)
         {
             throw std::domain_error("iterators do not fit");
@@ -5063,8 +5050,8 @@
     @return iterator pointing to the first element inserted, or @a pos if
     `ilist` is empty
 
-    @complexity Linear in `ilist.size()` plus linear in the distance between @a
-    pos and end of the container.
+    @complexity Linear in `ilist.size()` plus linear in the distance between
+    @a pos and end of the container.
 
     @liveexample{The example shows how `insert()` is used.,insert__ilist}
 
@@ -5709,8 +5696,8 @@
 
     @note A UTF-8 byte order mark is silently ignored.
 
-    @liveexample{The example below demonstrates the `parse()` function with and
-    without callback function.,parse__string__parser_callback_t}
+    @liveexample{The example below demonstrates the `parse()` function with
+    and without callback function.,parse__string__parser_callback_t}
 
     @sa @ref parse(std::istream&, parser_callback_t) for a version that reads
     from an input stream
@@ -5738,11 +5725,11 @@
 
     @note A UTF-8 byte order mark is silently ignored.
 
-    @liveexample{The example below demonstrates the `parse()` function with and
-    without callback function.,parse__istream__parser_callback_t}
+    @liveexample{The example below demonstrates the `parse()` function with
+    and without callback function.,parse__istream__parser_callback_t}
 
-    @sa @ref parse(const string_t&, parser_callback_t) for a version that reads
-    from a string
+    @sa @ref parse(const string_t&, parser_callback_t) for a version that
+    reads from a string
 
     @since version 1.0.0
     */
@@ -5875,8 +5862,8 @@
     /*!
     @brief escape a string
 
-    Escape a string by replacing certain special characters by a sequence of an
-    escape character (backslash) and another character and other control
+    Escape a string by replacing certain special characters by a sequence of
+    an escape character (backslash) and another character and other control
     characters by a sequence of "\u" followed by a four-digit hex
     representation.
 
@@ -5963,9 +5950,11 @@
                     {
                         // convert a number 0..15 to its hex representation
                         // (0..f)
-                        auto hexify = [](const char v) -> char
+                        const auto hexify = [](const int v) -> char
                         {
-                            return (v < 10) ? ('0' + v) : ('a' + v - 10);
+                            return (v < 10)
+                            ? ('0' + static_cast<char>(v))
+                            : ('a' + static_cast<char>((v - 10) & 0x1f));
                         };
 
                         // print character c as \uxxxx
@@ -5996,8 +5985,8 @@
 
     This function is called by the public member function dump and organizes
     the serialization internally. The indentation level is propagated as
-    additional parameter. In case of arrays and objects, the function is called
-    recursively. Note that
+    additional parameter. In case of arrays and objects, the function is
+    called recursively. Note that
 
     - strings and object keys are escaped using `escape_string()`
     - integer numbers are converted implicitly via `operator<<`
@@ -6127,79 +6116,23 @@
 
             case value_t::number_float:
             {
-                // check if number was parsed from a string
-                if (m_type.bits.parsed)
+                if (m_value.number_float == 0)
                 {
-                    // check if parsed number had an exponent given
-                    if (m_type.bits.has_exp)
-                    {
-                        // buffer size: precision (2^8-1 = 255) + other ('-.e-xxx' = 7) + null (1)
-                        char buf[263];
-                        int len;
-
-                        // handle capitalization of the exponent
-                        if (m_type.bits.exp_cap)
-                        {
-                            len = snprintf(buf, sizeof(buf), "%.*E",
-                                           m_type.bits.precision, m_value.number_float) + 1;
-                        }
-                        else
-                        {
-                            len = snprintf(buf, sizeof(buf), "%.*e",
-                                           m_type.bits.precision, m_value.number_float) + 1;
-                        }
-
-                        // remove '+' sign from the exponent if necessary
-                        if (not m_type.bits.exp_plus)
-                        {
-                            if (len > static_cast<int>(sizeof(buf)))
-                            {
-                                len = sizeof(buf);
-                            }
-                            for (int i = 0; i < len; i++)
-                            {
-                                if (buf[i] == '+')
-                                {
-                                    for (; i + 1 < len; i++)
-                                    {
-                                        buf[i] = buf[i + 1];
-                                    }
-                                }
-                            }
-                        }
-
-                        o << buf;
-                    }
-                    else
-                    {
-                        // no exponent - output as a decimal
-                        std::stringstream ss;
-                        ss.imbue(std::locale(std::locale(), new DecimalSeparator));  // fix locale problems
-                        ss << std::setprecision(m_type.bits.precision)
-                           << std::fixed << m_value.number_float;
-                        o << ss.str();
-                    }
+                    // special case for zero to get "0.0"/"-0.0"
+                    o << (std::signbit(m_value.number_float) ? "-0.0" : "0.0");
                 }
                 else
                 {
-                    if (m_value.number_float == 0)
-                    {
-                        // special case for zero to get "0.0"/"-0.0"
-                        o << (std::signbit(m_value.number_float) ? "-0.0" : "0.0");
-                    }
-                    else
-                    {
-                        // Otherwise 6, 15 or 16 digits of precision allows
-                        // round-trip IEEE 754 string->float->string,
-                        // string->double->string or string->long double->string;
-                        // to be safe, we read this value from
-                        // std::numeric_limits<number_float_t>::digits10
-                        std::stringstream ss;
-                        ss.imbue(std::locale(std::locale(), new DecimalSeparator));  // fix locale problems
-                        ss << std::setprecision(std::numeric_limits<double>::digits10)
-                           << m_value.number_float;
-                        o << ss.str();
-                    }
+                    // Otherwise 6, 15 or 16 digits of precision allows
+                    // round-trip IEEE 754 string->float->string,
+                    // string->double->string or string->long
+                    // double->string; to be safe, we read this value from
+                    // std::numeric_limits<number_float_t>::digits10
+                    std::stringstream ss;
+                    ss.imbue(std::locale(std::locale(), new DecimalSeparator));  // fix locale problems
+                    ss << std::setprecision(std::numeric_limits<double>::digits10)
+                       << m_value.number_float;
+                    o << ss.str();
                 }
                 return;
             }
@@ -6224,7 +6157,7 @@
     //////////////////////
 
     /// the type of the current element
-    type_data_t m_type = value_t::null;
+    value_t m_type = value_t::null;
 
     /// the value of the current element
     json_value m_value = {};
@@ -7000,13 +6933,13 @@
         }
 
         /// return a reference to the value pointed to by the iterator
-        reference operator*()
+        reference operator*() const
         {
             return const_cast<reference>(base_iterator::operator*());
         }
 
         /// dereference the iterator
-        pointer operator->()
+        pointer operator->() const
         {
             return const_cast<pointer>(base_iterator::operator->());
         }
@@ -7212,8 +7145,8 @@
     @brief lexical analysis
 
     This class organizes the lexical analysis during JSON deserialization. The
-    core of it is a scanner generated by [re2c](http://re2c.org) that processes
-    a buffer and recognizes tokens according to RFC 7159.
+    core of it is a scanner generated by [re2c](http://re2c.org) that
+    processes a buffer and recognizes tokens according to RFC 7159.
     */
     class lexer
     {
@@ -7222,17 +7155,17 @@
         enum class token_type
         {
             uninitialized,   ///< indicating the scanner is uninitialized
-            literal_true,    ///< the "true" literal
-            literal_false,   ///< the "false" literal
-            literal_null,    ///< the "null" literal
+            literal_true,    ///< the `true` literal
+            literal_false,   ///< the `false` literal
+            literal_null,    ///< the `null` literal
             value_string,    ///< a string -- use get_string() for actual value
             value_number,    ///< a number -- use get_number() for actual value
-            begin_array,     ///< the character for array begin "["
-            begin_object,    ///< the character for object begin "{"
-            end_array,       ///< the character for array end "]"
-            end_object,      ///< the character for object end "}"
-            name_separator,  ///< the name separator ":"
-            value_separator, ///< the value separator ","
+            begin_array,     ///< the character for array begin `[`
+            begin_object,    ///< the character for object begin `{`
+            end_array,       ///< the character for array end `]`
+            end_object,      ///< the character for object end `}`
+            name_separator,  ///< the name separator `:`
+            value_separator, ///< the value separator `,`
             parse_error,     ///< indicating a parse error
             end_of_input     ///< indicating the end of the input buffer
         };
@@ -7277,7 +7210,7 @@
 
         @return string representation of the code point
 
-        @throw std::out_of_range if code point is >0x10ffff; example: `"code
+        @throw std::out_of_range if code point is > 0x10ffff; example: `"code
         points above 0x10FFFF are invalid"`
         @throw std::invalid_argument if the low surrogate is invalid; example:
         `""missing or wrong low surrogate""`
@@ -7455,10 +7388,10 @@
                 number        { return token_type::value_number; }
 
                 // string
-                quotation_mark  = [\"];
+                quotation_mark  = ["];
                 escape          = [\\];
-                unescaped       = [^\"\\\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0A\x0B\x0C\x0D\x0E\x0F];
-                single_escaped  = [\"\\/bfnrt];
+                unescaped       = [^"\\\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0A\x0B\x0C\x0D\x0E\x0F];
+                single_escaped  = ["\\/bfnrt];
                 unicode_escaped = [u][0-9a-fA-F]{4};
                 escaped         = escape (single_escaped | unicode_escaped);
                 char            = unescaped | escaped;
@@ -7519,13 +7452,14 @@
 
         1. Escaped characters. In this case, a new character is constructed
            according to the nature of the escape. Some escapes create new
-           characters (e.g., `"\\n"` is replaced by `"\n"`), some are copied as
-           is (e.g., `"\\\\"`). Furthermore, Unicode escapes of the shape
+           characters (e.g., `"\\n"` is replaced by `"\n"`), some are copied
+           as is (e.g., `"\\\\"`). Furthermore, Unicode escapes of the shape
            `"\\uxxxx"` need special care. In this case, to_unicode takes care
            of the construction of the values.
         2. Unescaped characters are copied as is.
 
-        @return string value of current token without opening and closing quotes
+        @return string value of current token without opening and closing
+        quotes
         @throw std::out_of_range if to_unicode fails
         */
         string_t get_string() const
@@ -7636,8 +7570,8 @@
 
         This function (and its overloads) serves to select the most approprate
         standard floating point number parsing function based on the type
-        supplied via the first parameter.  Set this to
-        @a static_cast<number_float_t*>(nullptr).
+        supplied via the first parameter.  Set this to @a
+        static_cast<number_float_t*>(nullptr).
 
         @param[in] type  the @ref number_float_t in use
 
@@ -7661,8 +7595,8 @@
 
         This function (and its overloads) serves to select the most approprate
         standard floating point number parsing function based on the type
-        supplied via the first parameter.  Set this to
-        @a static_cast<number_float_t*>(nullptr).
+        supplied via the first parameter.  Set this to @a
+        static_cast<number_float_t*>(nullptr).
 
         @param[in] type  the @ref number_float_t in use
 
@@ -7681,8 +7615,8 @@
 
         This function (and its overloads) serves to select the most approprate
         standard floating point number parsing function based on the type
-        supplied via the first parameter.  Set this to
-        @a static_cast<number_float_t*>(nullptr).
+        supplied via the first parameter.  Set this to @a
+        static_cast<number_float_t*>(nullptr).
 
         @param[in] type  the @ref number_float_t in use
 
@@ -7706,22 +7640,16 @@
         This function parses the integer component up to the radix point or
         exponent while collecting information about the 'floating point
         representation', which it stores in the result parameter. If there is
-        no radix point or exponent, and the number can fit into a
-        @ref number_integer_t or @ref number_unsigned_t then it sets the
-        result parameter accordingly.
-
-        The 'floating point representation' includes the number of significant
-        figures after the radix point, whether the number is in exponential
-        or decimal form, the capitalization of the exponent marker, and if the
-        optional '+' is present in the exponent. This information is necessary
-        to perform accurate round trips of floating point numbers.
+        no radix point or exponent, and the number can fit into a @ref
+        number_integer_t or @ref number_unsigned_t then it sets the result
+        parameter accordingly.
 
         If the number is a floating point number the number is then parsed
         using @a std:strtod (or @a std:strtof or @a std::strtold).
 
         @param[out] result  @ref basic_json object to receive the number, or
-          NAN if the conversion read past the current token. The latter case
-          needs to be treated by the caller function.
+        NAN if the conversion read past the current token. The latter case
+        needs to be treated by the caller function.
         */
         void get_number(basic_json& result) const
         {
@@ -7729,15 +7657,6 @@
 
             const lexer::lexer_char_t* curptr = m_start;
 
-            // remember this number was parsed (for later serialization)
-            result.m_type.bits.parsed = true;
-
-            // 'found_radix_point' will be set to 0xFF upon finding a radix
-            // point and later used to mask in/out the precision depending
-            // whether a radix is found i.e. 'precision &= found_radix_point'
-            uint8_t found_radix_point = 0;
-            uint8_t precision = 0;
-
             // accumulate the integer conversion result (unsigned for now)
             number_unsigned_t value = 0;
 
@@ -7751,17 +7670,13 @@
             if (*curptr == '-')
             {
                 type = value_t::number_integer;
-                max = static_cast<uint64_t>(std::numeric_limits<number_integer_t>::max()) + 1;
+                max = static_cast<uint64_t>((std::numeric_limits<number_integer_t>::max)()) + 1;
                 curptr++;
             }
             else
             {
                 type = value_t::number_unsigned;
-                max = static_cast<uint64_t>(std::numeric_limits<number_unsigned_t>::max());
-                if (*curptr == '+')
-                {
-                    curptr++;
-                }
+                max = static_cast<uint64_t>((std::numeric_limits<number_unsigned_t>::max)());
             }
 
             // count the significant figures
@@ -7774,22 +7689,11 @@
                     {
                         // don't count '.' but change to float
                         type = value_t::number_float;
-
-                        // reset precision count
-                        precision = 0;
-                        found_radix_point = 0xFF;
                         continue;
                     }
                     // assume exponent (if not then will fail parse): change to
                     // float, stop counting and record exponent details
                     type = value_t::number_float;
-                    result.m_type.bits.has_exp = true;
-
-                    // exponent capitalization
-                    result.m_type.bits.exp_cap = (*curptr == 'E');
-
-                    // exponent '+' sign
-                    result.m_type.bits.exp_plus = (*(++curptr) == '+');
                     break;
                 }
 
@@ -7811,13 +7715,8 @@
                         value = temp;
                     }
                 }
-                ++precision;
             }
 
-            // If no radix point was found then precision would now be set to
-            // the number of digits, which is wrong - clear it.
-            result.m_type.bits.precision = precision & found_radix_point;
-
             // save the value (if not a float)
             if (type == value_t::number_unsigned)
             {
@@ -8123,6 +8022,1232 @@
         /// the lexer
         lexer m_lexer;
     };
+
+  public:
+    /*!
+    @brief JSON Pointer
+
+    A JSON pointer defines a string syntax for identifying a specific value
+    within a JSON document. It can be used with functions `at` and
+    `operator[]`. Furthermore, JSON pointers are the base for JSON patches.
+
+    @sa [RFC 6901](https://tools.ietf.org/html/rfc6901)
+
+    @since version 2.0.0
+    */
+    class json_pointer
+    {
+        /// allow basic_json to access private members
+        friend class basic_json;
+
+      public:
+        /*!
+        @brief create JSON pointer
+
+        Create a JSON pointer according to the syntax described in
+        [Section 3 of RFC6901](https://tools.ietf.org/html/rfc6901#section-3).
+
+        @param[in] s  string representing the JSON pointer; if omitted, the
+                      empty string is assumed which references the whole JSON
+                      value
+
+        @throw std::domain_error if reference token is nonempty and does not
+        begin with a slash (`/`); example: `"JSON pointer must be empty or
+        begin with /"`
+        @throw std::domain_error if a tilde (`~`) is not followed by `0`
+        (representing `~`) or `1` (representing `/`); example: `"escape error:
+        ~ must be followed with 0 or 1"`
+
+        @liveexample{The example shows the construction several valid JSON
+        pointers as well as the exceptional behavior.,json_pointer}
+
+        @since version 2.0.0
+        */
+        explicit json_pointer(const std::string& s = "")
+            : reference_tokens(split(s))
+        {}
+
+        /*!
+        @brief return a string representation of the JSON pointer
+
+        @invariant For each JSON pointer `ptr`, it holds:
+        @code {.cpp}
+        ptr == json_pointer(ptr.to_string());
+        @endcode
+
+        @return a string representation of the JSON pointer
+
+        @liveexample{The example shows the result of `to_string`.,
+        json_pointer__to_string}
+
+        @since version 2.0.0
+        */
+        std::string to_string() const noexcept
+        {
+            std::string result;
+
+            for (const auto& reference_token : reference_tokens)
+            {
+                result += "/" + escape(reference_token);
+            }
+
+            return result;
+        }
+
+        /// @copydoc to_string()
+        operator std::string() const
+        {
+            return to_string();
+        }
+
+      private:
+        /// remove and return last reference pointer
+        std::string pop_back()
+        {
+            if (is_root())
+            {
+                throw std::domain_error("JSON pointer has no parent");
+            }
+
+            auto last = reference_tokens.back();
+            reference_tokens.pop_back();
+            return last;
+        }
+
+        /// return whether pointer points to the root document
+        bool is_root() const
+        {
+            return reference_tokens.empty();
+        }
+
+        json_pointer top() const
+        {
+            if (is_root())
+            {
+                throw std::domain_error("JSON pointer has no parent");
+            }
+
+            json_pointer result = *this;
+            result.reference_tokens = {reference_tokens[0]};
+            return result;
+        }
+
+        /*!
+        @brief create and return a reference to the pointed to value
+        */
+        reference get_and_create(reference j) const
+        {
+            pointer result = &j;
+
+            // in case no reference tokens exist, return a reference to the
+            // JSON value j which will be overwritten by a primitive value
+            for (const auto& reference_token : reference_tokens)
+            {
+                switch (result->m_type)
+                {
+                    case value_t::null:
+                    {
+                        if (reference_token == "0")
+                        {
+                            // start a new array if reference token is 0
+                            result = &result->operator[](0);
+                        }
+                        else
+                        {
+                            // start a new object otherwise
+                            result = &result->operator[](reference_token);
+                        }
+                        break;
+                    }
+
+                    case value_t::object:
+                    {
+                        // create an entry in the object
+                        result = &result->operator[](reference_token);
+                        break;
+                    }
+
+                    case value_t::array:
+                    {
+                        // create an entry in the array
+                        result = &result->operator[](static_cast<size_type>(std::stoi(reference_token)));
+                        break;
+                    }
+
+                    /*
+                    The following code is only reached if there exists a
+                    reference token _and_ the current value is primitive. In
+                    this case, we have an error situation, because primitive
+                    values may only occur as single value; that is, with an
+                    empty list of reference tokens.
+                    */
+                    default:
+                    {
+                        throw std::domain_error("invalid value to unflatten");
+                    }
+                }
+            }
+
+            return *result;
+        }
+
+        /*!
+        @brief return a reference to the pointed to value
+
+        @param[in] ptr  a JSON value
+
+        @return reference to the JSON value pointed to by the JSON pointer
+
+        @complexity Linear in the length of the JSON pointer.
+
+        @throw std::out_of_range      if the JSON pointer can not be resolved
+        @throw std::domain_error      if an array index begins with '0'
+        @throw std::invalid_argument  if an array index was not a number
+        */
+        reference get_unchecked(pointer ptr) const
+        {
+            for (const auto& reference_token : reference_tokens)
+            {
+                switch (ptr->m_type)
+                {
+                    case value_t::object:
+                    {
+                        // use unchecked object access
+                        ptr = &ptr->operator[](reference_token);
+                        break;
+                    }
+
+                    case value_t::array:
+                    {
+                        // error condition (cf. RFC 6901, Sect. 4)
+                        if (reference_token.size() > 1 and reference_token[0] == '0')
+                        {
+                            throw std::domain_error("array index must not begin with '0'");
+                        }
+
+                        if (reference_token == "-")
+                        {
+                            // explicityly treat "-" as index beyond the end
+                            ptr = &ptr->operator[](ptr->m_value.array->size());
+                        }
+                        else
+                        {
+                            // convert array index to number; unchecked access
+                            ptr = &ptr->operator[](static_cast<size_type>(std::stoi(reference_token)));
+                        }
+                        break;
+                    }
+
+                    default:
+                    {
+                        throw std::out_of_range("unresolved reference token '" + reference_token + "'");
+                    }
+                }
+            }
+
+            return *ptr;
+        }
+
+        reference get_checked(pointer ptr) const
+        {
+            for (const auto& reference_token : reference_tokens)
+            {
+                switch (ptr->m_type)
+                {
+                    case value_t::object:
+                    {
+                        // note: at performs range check
+                        ptr = &ptr->at(reference_token);
+                        break;
+                    }
+
+                    case value_t::array:
+                    {
+                        if (reference_token == "-")
+                        {
+                            // "-" always fails the range check
+                            throw std::out_of_range("array index '-' (" +
+                                                    std::to_string(ptr->m_value.array->size()) +
+                                                    ") is out of range");
+                        }
+
+                        // error condition (cf. RFC 6901, Sect. 4)
+                        if (reference_token.size() > 1 and reference_token[0] == '0')
+                        {
+                            throw std::domain_error("array index must not begin with '0'");
+                        }
+
+                        // note: at performs range check
+                        ptr = &ptr->at(static_cast<size_type>(std::stoi(reference_token)));
+                        break;
+                    }
+
+                    default:
+                    {
+                        throw std::out_of_range("unresolved reference token '" + reference_token + "'");
+                    }
+                }
+            }
+
+            return *ptr;
+        }
+
+        /*!
+        @brief return a const reference to the pointed to value
+
+        @param[in] ptr  a JSON value
+
+        @return const reference to the JSON value pointed to by the JSON
+                pointer
+        */
+        const_reference get_unchecked(const_pointer ptr) const
+        {
+            for (const auto& reference_token : reference_tokens)
+            {
+                switch (ptr->m_type)
+                {
+                    case value_t::object:
+                    {
+                        // use unchecked object access
+                        ptr = &ptr->operator[](reference_token);
+                        break;
+                    }
+
+                    case value_t::array:
+                    {
+                        if (reference_token == "-")
+                        {
+                            // "-" cannot be used for const access
+                            throw std::out_of_range("array index '-' (" +
+                                                    std::to_string(ptr->m_value.array->size()) +
+                                                    ") is out of range");
+                        }
+
+                        // error condition (cf. RFC 6901, Sect. 4)
+                        if (reference_token.size() > 1 and reference_token[0] == '0')
+                        {
+                            throw std::domain_error("array index must not begin with '0'");
+                        }
+
+                        // use unchecked array access
+                        ptr = &ptr->operator[](static_cast<size_type>(std::stoi(reference_token)));
+                        break;
+                    }
+
+                    default:
+                    {
+                        throw std::out_of_range("unresolved reference token '" + reference_token + "'");
+                    }
+                }
+            }
+
+            return *ptr;
+        }
+
+        const_reference get_checked(const_pointer ptr) const
+        {
+            for (const auto& reference_token : reference_tokens)
+            {
+                switch (ptr->m_type)
+                {
+                    case value_t::object:
+                    {
+                        // note: at performs range check
+                        ptr = &ptr->at(reference_token);
+                        break;
+                    }
+
+                    case value_t::array:
+                    {
+                        if (reference_token == "-")
+                        {
+                            // "-" always fails the range check
+                            throw std::out_of_range("array index '-' (" +
+                                                    std::to_string(ptr->m_value.array->size()) +
+                                                    ") is out of range");
+                        }
+
+                        // error condition (cf. RFC 6901, Sect. 4)
+                        if (reference_token.size() > 1 and reference_token[0] == '0')
+                        {
+                            throw std::domain_error("array index must not begin with '0'");
+                        }
+
+                        // note: at performs range check
+                        ptr = &ptr->at(static_cast<size_type>(std::stoi(reference_token)));
+                        break;
+                    }
+
+                    default:
+                    {
+                        throw std::out_of_range("unresolved reference token '" + reference_token + "'");
+                    }
+                }
+            }
+
+            return *ptr;
+        }
+
+        /// split the string input to reference tokens
+        static std::vector<std::string> split(std::string reference_string)
+        {
+            std::vector<std::string> result;
+
+            // special case: empty reference string -> no reference tokens
+            if (reference_string.empty())
+            {
+                return result;
+            }
+
+            // check if nonempty reference string begins with slash
+            if (reference_string[0] != '/')
+            {
+                throw std::domain_error("JSON pointer must be empty or begin with '/'");
+            }
+
+            // extract the reference tokens:
+            // - slash: position of the last read slash (or end of string)
+            // - start: position after the previous slash
+            for (
+                // search for the first slash after the first character
+                size_t slash = reference_string.find_first_of("/", 1),
+                // set the beginning of the first reference token
+                start = 1;
+                // we can stop if start == string::npos+1 = 0
+                start != 0;
+                // set the beginning of the next reference token
+                // (will eventually be 0 if slash == std::string::npos)
+                start = slash + 1,
+                // find next slash
+                slash = reference_string.find_first_of("/", start))
+            {
+                // use the text between the beginning of the reference token
+                // (start) and the last slash (slash).
+                auto reference_token = reference_string.substr(start, slash - start);
+
+                // check reference tokens are properly escaped
+                for (size_t pos = reference_token.find_first_of("~");
+                        pos != std::string::npos;
+                        pos = reference_token.find_first_of("~", pos + 1))
+                {
+                    assert(reference_token[pos] == '~');
+
+                    // ~ must be followed by 0 or 1
+                    if (pos == reference_token.size() - 1 or
+                            (reference_token[pos + 1] != '0' and
+                             reference_token[pos + 1] != '1'))
+                    {
+                        throw std::domain_error("escape error: '~' must be followed with '0' or '1'");
+                    }
+                }
+
+                // finally, store the reference token
+                unescape(reference_token);
+                result.push_back(reference_token);
+            }
+
+            return result;
+        }
+
+      private:
+        /*!
+        @brief replace all occurrences of a substring by another string
+
+        @param[in,out] s  the string to manipulate
+        @param[in]     f  the substring to replace with @a t
+        @param[out]    t  the string to replace @a f
+
+        @return The string @a s where all occurrences of @a f are replaced
+                with @a t.
+
+        @pre The search string @a f must not be empty.
+
+        @since version 2.0.0
+        */
+        static void replace_substring(std::string& s,
+                                      const std::string& f,
+                                      const std::string& t)
+        {
+            assert(not f.empty());
+
+            for (
+                size_t pos = s.find(f);         // find first occurrence of f
+                pos != std::string::npos;       // make sure f was found
+                s.replace(pos, f.size(), t),    // replace with t
+                pos = s.find(f, pos + t.size()) // find next occurrence of f
+            );
+        }
+
+        /// escape tilde and slash
+        static std::string escape(std::string s)
+        {
+            // escape "~"" to "~0" and "/" to "~1"
+            replace_substring(s, "~", "~0");
+            replace_substring(s, "/", "~1");
+            return s;
+        }
+
+        /// unescape tilde and slash
+        static void unescape(std::string& s)
+        {
+            // first transform any occurrence of the sequence '~1' to '/'
+            replace_substring(s, "~1", "/");
+            // then transform any occurrence of the sequence '~0' to '~'
+            replace_substring(s, "~0", "~");
+        }
+
+        /*!
+        @param[in] reference_string  the reference string to the current value
+        @param[in] value             the value to consider
+        @param[in,out] result        the result object to insert values to
+
+        @note Empty objects or arrays are flattened to `null`.
+        */
+        static void flatten(const std::string& reference_string,
+                            const basic_json& value,
+                            basic_json& result)
+        {
+            switch (value.m_type)
+            {
+                case value_t::array:
+                {
+                    if (value.m_value.array->empty())
+                    {
+                        // flatten empty array as null
+                        result[reference_string] = nullptr;
+                    }
+                    else
+                    {
+                        // iterate array and use index as reference string
+                        for (size_t i = 0; i < value.m_value.array->size(); ++i)
+                        {
+                            flatten(reference_string + "/" + std::to_string(i),
+                                    value.m_value.array->operator[](i), result);
+                        }
+                    }
+                    break;
+                }
+
+                case value_t::object:
+                {
+                    if (value.m_value.object->empty())
+                    {
+                        // flatten empty object as null
+                        result[reference_string] = nullptr;
+                    }
+                    else
+                    {
+                        // iterate object and use keys as reference string
+                        for (const auto& element : *value.m_value.object)
+                        {
+                            flatten(reference_string + "/" + escape(element.first),
+                                    element.second, result);
+                        }
+                    }
+                    break;
+                }
+
+                default:
+                {
+                    // add primitive value with its reference string
+                    result[reference_string] = value;
+                    break;
+                }
+            }
+        }
+
+        /*!
+        @param[in] value  flattened JSON
+
+        @return unflattened JSON
+        */
+        static basic_json unflatten(const basic_json& value)
+        {
+            if (not value.is_object())
+            {
+                throw std::domain_error("only objects can be unflattened");
+            }
+
+            basic_json result;
+
+            // iterate the JSON object values
+            for (const auto& element : *value.m_value.object)
+            {
+                if (not element.second.is_primitive())
+                {
+                    throw std::domain_error("values in object must be primitive");
+                }
+
+                // assign value to reference pointed to by JSON pointer; Note
+                // that if the JSON pointer is "" (i.e., points to the whole
+                // value), function get_and_create returns a reference to
+                // result itself. An assignment will then create a primitive
+                // value.
+                json_pointer(element.first).get_and_create(result) = element.second;
+            }
+
+            return result;
+        }
+
+      private:
+        /// the reference tokens
+        std::vector<std::string> reference_tokens {};
+    };
+
+    //////////////////////////
+    // JSON Pointer support //
+    //////////////////////////
+
+    /// @name JSON Pointer functions
+    /// @{
+
+    /*!
+    @brief access specified element via JSON Pointer
+
+    Uses a JSON pointer to retrieve a reference to the respective JSON value.
+    No bound checking is performed. Similar to @ref operator[](const typename
+    object_t::key_type&), `null` values are created in arrays and objects if
+    necessary.
+
+    In particular:
+    - If the JSON pointer points to an object key that does not exist, it
+      is created an filled with a `null` value before a reference to it
+      is returned.
+    - If the JSON pointer points to an array index that does not exist, it
+      is created an filled with a `null` value before a reference to it
+      is returned. All indices between the current maximum and the given
+      index are also filled with `null`.
+    - The special value `-` is treated as a synonym for the index past the
+      end.
+
+    @param[in] ptr  a JSON pointer
+
+    @return reference to the element pointed to by @a ptr
+
+    @complexity Constant.
+
+    @throw std::out_of_range      if the JSON pointer can not be resolved
+    @throw std::domain_error      if an array index begins with '0'
+    @throw std::invalid_argument  if an array index was not a number
+
+    @liveexample{The behavior is shown in the example.,operatorjson_pointer}
+
+    @since version 2.0.0
+    */
+    reference operator[](const json_pointer& ptr)
+    {
+        return ptr.get_unchecked(this);
+    }
+
+    /*!
+    @brief access specified element via JSON Pointer
+
+    Uses a JSON pointer to retrieve a reference to the respective JSON value.
+    No bound checking is performed. The function does not change the JSON
+    value; no `null` values are created. In particular, the the special value
+    `-` yields an exception.
+
+    @param[in] ptr  JSON pointer to the desired element
+
+    @return const reference to the element pointed to by @a ptr
+
+    @complexity Constant.
+
+    @throw std::out_of_range      if the JSON pointer can not be resolved
+    @throw std::domain_error      if an array index begins with '0'
+    @throw std::invalid_argument  if an array index was not a number
+
+    @liveexample{The behavior is shown in the example.,operatorjson_pointer_const}
+
+    @since version 2.0.0
+    */
+    const_reference operator[](const json_pointer& ptr) const
+    {
+        return ptr.get_unchecked(this);
+    }
+
+    /*!
+    @brief access specified element via JSON Pointer
+
+    Returns a reference to the element at with specified JSON pointer @a ptr,
+    with bounds checking.
+
+    @param[in] ptr  JSON pointer to the desired element
+
+    @return reference to the element pointed to by @a ptr
+
+    @complexity Constant.
+
+    @throw std::out_of_range      if the JSON pointer can not be resolved
+    @throw std::domain_error      if an array index begins with '0'
+    @throw std::invalid_argument  if an array index was not a number
+
+    @liveexample{The behavior is shown in the example.,at_json_pointer}
+
+    @since version 2.0.0
+    */
+    reference at(const json_pointer& ptr)
+    {
+        return ptr.get_checked(this);
+    }
+
+    /*!
+    @brief access specified element via JSON Pointer
+
+    Returns a const reference to the element at with specified JSON pointer @a
+    ptr, with bounds checking.
+
+    @param[in] ptr  JSON pointer to the desired element
+
+    @return reference to the element pointed to by @a ptr
+
+    @complexity Constant.
+
+    @throw std::out_of_range      if the JSON pointer can not be resolved
+    @throw std::domain_error      if an array index begins with '0'
+    @throw std::invalid_argument  if an array index was not a number
+
+    @liveexample{The behavior is shown in the example.,at_json_pointer_const}
+
+    @since version 2.0.0
+    */
+    const_reference at(const json_pointer& ptr) const
+    {
+        return ptr.get_checked(this);
+    }
+
+    /*!
+    @brief return flattened JSON value
+
+    The function creates a JSON object whose keys are JSON pointers (see [RFC
+    6901](https://tools.ietf.org/html/rfc6901)) and whose values are all
+    primitive. The original JSON value can be restored using the @ref
+    unflatten() function.
+
+    @return an object that maps JSON pointers to primitve values
+
+    @note Empty objects and arrays are flattened to `null` and will not be
+          reconstructed correctly by the @ref unflatten() function.
+
+    @complexity Linear in the size the JSON value.
+
+    @liveexample{The following code shows how a JSON object is flattened to an
+    object whose keys consist of JSON pointers.,flatten}
+
+    @sa @ref unflatten() for the reverse function
+
+    @since version 2.0.0
+    */
+    basic_json flatten() const
+    {
+        basic_json result(value_t::object);
+        json_pointer::flatten("", *this, result);
+        return result;
+    }
+
+    /*!
+    @brief unflatten a previously flattened JSON value
+
+    The function restores the arbitrary nesting of a JSON value that has been
+    flattened before using the @ref flatten() function. The JSON value must
+    meet certain constraints:
+    1. The value must be an object.
+    2. The keys must be JSON pointers (see
+       [RFC 6901](https://tools.ietf.org/html/rfc6901))
+    3. The mapped values must be primitive JSON types.
+
+    @return the original JSON from a flattened version
+
+    @note Empty objects and arrays are flattened by @ref flatten() to `null`
+          values and can not unflattened to their original type. Apart from
+          this example, for a JSON value `j`, the following is always true:
+          `j == j.flatten().unflatten()`.
+
+    @complexity Linear in the size the JSON value.
+
+    @liveexample{The following code shows how a flattened JSON object is
+    unflattened into the original nested JSON object.,unflatten}
+
+    @sa @ref flatten() for the reverse function
+
+    @since version 2.0.0
+    */
+    basic_json unflatten() const
+    {
+        return json_pointer::unflatten(*this);
+    }
+
+    /// @}
+
+    //////////////////////////
+    // JSON Patch functions //
+    //////////////////////////
+
+    /// @name JSON Patch functions
+    /// @{
+
+    /*!
+    @brief applies a JSON patch
+
+    [JSON Patch](http://jsonpatch.com) defines a JSON document structure for
+    expressing a sequence of operations to apply to a JSON) document. With
+    this funcion, a JSON Patch is applied to the current JSON value by
+    executing all operations from the patch.
+
+    @param[in] json_patch  JSON patch document
+    @return patched document
+
+    @note The application of a patch is atomic: Either all operations succeed
+          and the patched document is returned or an exception is thrown. In
+          any case, the original value is not changed: the patch is applied
+          to a copy of the value.
+
+    @throw std::out_of_range if a JSON pointer inside the patch could not
+    be resolved successfully in the current JSON value; example: `"key baz
+    not found"`
+    @throw invalid_argument if the JSON patch is malformed (e.g., mandatory
+    attributes are missing); example: `"operation add must have member path"`
+
+    @complexity Linear in the size of the JSON value and the length of the
+    JSON patch. As usually only a fraction of the JSON value is affected by
+    the patch, the complexity can usually be neglected.
+
+    @liveexample{The following code shows how a JSON patch is applied to a
+    value.,patch}
+
+    @sa @ref diff -- create a JSON patch by comparing two JSON values
+
+    @sa [RFC 6902 (JSON Patch)](https://tools.ietf.org/html/rfc6902)
+    @sa [RFC 6901 (JSON Pointer)](https://tools.ietf.org/html/rfc6901)
+
+    @since version 2.0.0
+    */
+    basic_json patch(const basic_json& json_patch) const
+    {
+        // make a working copy to apply the patch to
+        basic_json result = *this;
+
+        // the valid JSON Patch operations
+        enum class patch_operations {add, remove, replace, move, copy, test, invalid};
+
+        const auto get_op = [](const std::string op)
+        {
+            if (op == "add")
+            {
+                return patch_operations::add;
+            }
+            if (op == "remove")
+            {
+                return patch_operations::remove;
+            }
+            if (op == "replace")
+            {
+                return patch_operations::replace;
+            }
+            if (op == "move")
+            {
+                return patch_operations::move;
+            }
+            if (op == "copy")
+            {
+                return patch_operations::copy;
+            }
+            if (op == "test")
+            {
+                return patch_operations::test;
+            }
+
+            return patch_operations::invalid;
+        };
+
+        // wrapper for "add" operation; add value at ptr
+        const auto operation_add = [&result](json_pointer & ptr, basic_json val)
+        {
+            // adding to the root of the target document means replacing it
+            if (ptr.is_root())
+            {
+                result = val;
+            }
+            else
+            {
+                // make sure the top element of the pointer exists
+                json_pointer top_pointer = ptr.top();
+                if (top_pointer != ptr)
+                {
+                    basic_json& x = result.at(top_pointer);
+                }
+
+                // get reference to parent of JSON pointer ptr
+                const auto last_path = ptr.pop_back();
+                basic_json& parent = result[ptr];
+
+                switch (parent.m_type)
+                {
+                    case value_t::null:
+                    case value_t::object:
+                    {
+                        // use operator[] to add value
+                        parent[last_path] = val;
+                        break;
+                    }
+
+                    case value_t::array:
+                    {
+                        if (last_path == "-")
+                        {
+                            // special case: append to back
+                            parent.push_back(val);
+                        }
+                        else
+                        {
+                            const auto idx = std::stoi(last_path);
+                            if (static_cast<size_type>(idx) > parent.size())
+                            {
+                                // avoid undefined behavior
+                                throw std::out_of_range("array index " + std::to_string(idx) + " is out of range");
+                            }
+                            else
+                            {
+                                // default case: insert add offset
+                                parent.insert(parent.begin() + static_cast<difference_type>(idx), val);
+                            }
+                        }
+                        break;
+                    }
+
+                    default:
+                    {
+                        // if there exists a parent it cannot be primitive
+                        assert(false);  // LCOV_EXCL_LINE
+                    }
+                }
+            }
+        };
+
+        // wrapper for "remove" operation; remove value at ptr
+        const auto operation_remove = [&result](json_pointer & ptr)
+        {
+            // get reference to parent of JSON pointer ptr
+            const auto last_path = ptr.pop_back();
+            basic_json& parent = result.at(ptr);
+
+            // remove child
+            if (parent.is_object())
+            {
+                // perform range check
+                auto it = parent.find(last_path);
+                if (it != parent.end())
+                {
+                    parent.erase(it);
+                }
+                else
+                {
+                    throw std::out_of_range("key '" + last_path + "' not found");
+                }
+            }
+            else if (parent.is_array())
+            {
+                // note erase performs range check
+                parent.erase(static_cast<size_type>(std::stoi(last_path)));
+            }
+        };
+
+        // type check
+        if (not json_patch.is_array())
+        {
+            // a JSON patch must be an array of objects
+            throw std::invalid_argument("JSON patch must be an array of objects");
+        }
+
+        // iterate and apply th eoperations
+        for (const auto& val : json_patch)
+        {
+            // 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&
+            {
+                // find value
+                auto it = val.m_value.object->find(member);
+
+                // context-sensitive error message
+                const auto error_msg = (op == "op") ? "operation" : "operation '" + op + "'";
+
+                // check if desired value is present
+                if (it == val.m_value.object->end())
+                {
+                    throw std::invalid_argument(error_msg + " must have member '" + member + "'");
+                }
+
+                // check if result is of type string
+                if (string_type and not it->second.is_string())
+                {
+                    throw std::invalid_argument(error_msg + " must have string member '" + member + "'");
+                }
+
+                // no error: return value
+                return it->second;
+            };
+
+            // type check
+            if (not val.is_object())
+            {
+                throw std::invalid_argument("JSON patch must be an array of objects");
+            }
+
+            // collect mandatory members
+            const std::string op = get_value("op", "op", true);
+            const std::string path = get_value(op, "path", true);
+            json_pointer ptr(path);
+
+            switch (get_op(op))
+            {
+                case patch_operations::add:
+                {
+                    operation_add(ptr, get_value("add", "value", false));
+                    break;
+                }
+
+                case patch_operations::remove:
+                {
+                    operation_remove(ptr);
+                    break;
+                }
+
+                case patch_operations::replace:
+                {
+                    // the "path" location must exist - use at()
+                    result.at(ptr) = get_value("replace", "value", false);
+                    break;
+                }
+
+                case patch_operations::move:
+                {
+                    const std::string from_path = get_value("move", "from", true);
+                    json_pointer from_ptr(from_path);
+
+                    // the "from" location must exist - use at()
+                    basic_json v = result.at(from_ptr);
+
+                    // The move operation is functionally identical to a
+                    // "remove" operation on the "from" location, followed
+                    // immediately by an "add" operation at the target
+                    // location with the value that was just removed.
+                    operation_remove(from_ptr);
+                    operation_add(ptr, v);
+                    break;
+                }
+
+                case patch_operations::copy:
+                {
+                    const std::string from_path = get_value("copy", "from", true);;
+                    const json_pointer from_ptr(from_path);
+
+                    // the "from" location must exist - use at()
+                    result[ptr] = result.at(from_ptr);
+                    break;
+                }
+
+                case patch_operations::test:
+                {
+                    bool success = false;
+                    try
+                    {
+                        // check if "value" matches the one at "path"
+                        // the "path" location must exist - use at()
+                        success = (result.at(ptr) == get_value("test", "value", false));
+                    }
+                    catch (std::out_of_range&)
+                    {
+                        // ignore out of range errors: success remains false
+                    }
+
+                    // throw an exception if test fails
+                    if (not success)
+                    {
+                        throw std::domain_error("unsuccessful: " + val.dump());
+                    }
+
+                    break;
+                }
+
+                case patch_operations::invalid:
+                {
+                    // op must be "add", "remove", "replace", "move", "copy", or
+                    // "test"
+                    throw std::invalid_argument("operation value '" + op + "' is invalid");
+                }
+            }
+        }
+
+        return result;
+    }
+
+    /*!
+    @brief creates a diff as a JSON patch
+
+    Creates a [JSON Patch](http://jsonpatch.com) so that value @a source can
+    be changed into the value @a target by calling @ref patch function.
+
+    @invariant For two JSON values @a source and @a target, the following code
+    yields always `true`:
+    @code {.cpp}
+    source.patch(diff(source, target)) == target;
+    @endcode
+
+    @note Currently, only `remove`, `add`, and `replace` operations are
+          generated.
+
+    @param[in] source  JSON value to copare from
+    @param[in] target  JSON value to copare against
+    @param[in] path    helper value to create JSON pointers
+
+    @return a JSON patch to convert the @a source to @a target
+
+    @complexity Linear in the lengths of @a source and @a target.
+
+    @liveexample{The following code shows how a JSON patch is created as a
+    diff for two JSON values.,diff}
+
+    @sa @ref patch -- apply a JSON patch
+
+    @sa [RFC 6902 (JSON Patch)](https://tools.ietf.org/html/rfc6902)
+
+    @since version 2.0.0
+    */
+    static basic_json diff(const basic_json& source,
+                           const basic_json& target,
+                           std::string path = "")
+    {
+        // the patch
+        basic_json result(value_t::array);
+
+        // if the values are the same, return empty patch
+        if (source == target)
+        {
+            return result;
+        }
+
+        if (source.type() != target.type())
+        {
+            // different types: replace value
+            result.push_back(
+            {
+                {"op", "replace"},
+                {"path", path},
+                {"value", target}
+            });
+        }
+        else
+        {
+            switch (source.type())
+            {
+                case value_t::array:
+                {
+                    // first pass: traverse common elements
+                    size_t i = 0;
+                    while (i < source.size() and i < target.size())
+                    {
+                        // recursive call to compare array values at index i
+                        auto temp_diff = diff(source[i], target[i], path + "/" + std::to_string(i));
+                        result.insert(result.end(), temp_diff.begin(), temp_diff.end());
+                        ++i;
+                    }
+
+                    // i now reached the end of at least one array
+                    // in a second pass, traverse the remaining elements
+
+                    // remove my remaining elements
+                    const auto end_index = static_cast<difference_type>(result.size());
+                    while (i < source.size())
+                    {
+                        // add operations in reverse order to avoid invalid
+                        // indices
+                        result.insert(result.begin() + end_index, object(
+                        {
+                            {"op", "remove"},
+                            {"path", path + "/" + std::to_string(i)}
+                        }));
+                        ++i;
+                    }
+
+                    // add other remaining elements
+                    while (i < target.size())
+                    {
+                        result.push_back(
+                        {
+                            {"op", "add"},
+                            {"path", path + "/" + std::to_string(i)},
+                            {"value", target[i]}
+                        });
+                        ++i;
+                    }
+
+                    break;
+                }
+
+                case value_t::object:
+                {
+                    // first pass: traverse this object's elements
+                    for (auto it = source.begin(); it != source.end(); ++it)
+                    {
+                        // escape the key name to be used in a JSON patch
+                        const auto key = json_pointer::escape(it.key());
+
+                        if (target.find(it.key()) != target.end())
+                        {
+                            // recursive call to compare object values at key it
+                            auto temp_diff = diff(it.value(), target[it.key()], path + "/" + key);
+                            result.insert(result.end(), temp_diff.begin(), temp_diff.end());
+                        }
+                        else
+                        {
+                            // found a key that is not in o -> remove it
+                            result.push_back(object(
+                            {
+                                {"op", "remove"},
+                                {"path", path + "/" + key}
+                            }));
+                        }
+                    }
+
+                    // second pass: traverse other object's elements
+                    for (auto it = target.begin(); it != target.end(); ++it)
+                    {
+                        if (source.find(it.key()) == source.end())
+                        {
+                            // found a key that is not in this -> add it
+                            const auto key = json_pointer::escape(it.key());
+                            result.push_back(
+                            {
+                                {"op", "add"},
+                                {"path", path + "/" + key},
+                                {"value", it.value()}
+                            });
+                        }
+                    }
+
+                    break;
+                }
+
+                default:
+                {
+                    // both primitive type: replace value
+                    result.push_back(
+                    {
+                        {"op", "replace"},
+                        {"path", path},
+                        {"value", target}
+                    });
+                    break;
+                }
+            }
+        }
+
+        return result;
+    }
+
+    /// @}
 };
 
 
@@ -8133,8 +9258,8 @@
 /*!
 @brief default JSON class
 
-This type is the default specialization of the @ref basic_json class which uses
-the standard template types.
+This type is the default specialization of the @ref basic_json class which
+uses the standard template types.
 
 @since version 1.0.0
 */
@@ -8142,9 +9267,9 @@
 }
 
 
-/////////////////////////
-// nonmember functions //
-/////////////////////////
+///////////////////////
+// nonmember support //
+///////////////////////
 
 // specialization of std::swap, and std::hash
 namespace std
@@ -8185,9 +9310,9 @@
 /*!
 @brief user-defined string literal for JSON values
 
-This operator implements a user-defined string literal for JSON objects. It can
-be used by adding \p "_json" to a string literal and returns a JSON object if
-no parse error occurred.
+This operator implements a user-defined string literal for JSON objects. It
+can be used by adding \p "_json" to a string literal and returns a JSON object
+if no parse error occurred.
 
 @param[in] s  a string representation of a JSON object
 @return a JSON object
@@ -8199,6 +9324,16 @@
     return nlohmann::json::parse(reinterpret_cast<const nlohmann::json::string_t::value_type*>(s));
 }
 
+/*!
+@brief user-defined string literal for JSON pointer
+
+@since version 2.0.0
+*/
+inline nlohmann::json::json_pointer operator "" _json_pointer(const char* s, std::size_t)
+{
+    return nlohmann::json::json_pointer(s);
+}
+
 // restore GCC/clang diagnostic settings
 #if defined(__clang__) || defined(__GNUC__) || defined(__GNUG__)
     #pragma GCC diagnostic pop
diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt
new file mode 100644
index 0000000..c66b19c
--- /dev/null
+++ b/test/CMakeLists.txt
@@ -0,0 +1,25 @@
+# The unit test executable.
+set(JSON_UNITTEST_TARGET_NAME "json_unit")
+add_executable(${JSON_UNITTEST_TARGET_NAME}
+    "src/catch.hpp"
+    "src/unit.cpp"
+)
+
+set_target_properties(${JSON_UNITTEST_TARGET_NAME} PROPERTIES
+    CXX_STANDARD 11
+    CXX_STANDARD_REQUIRED ON
+    COMPILE_DEFINITIONS "$<$<CXX_COMPILER_ID:MSVC>:_SCL_SECURE_NO_WARNINGS>"
+    COMPILE_OPTIONS "$<$<CXX_COMPILER_ID:MSVC>:/EHsc;$<$<CONFIG:Release>:/Od>>"
+)
+
+target_include_directories(${JSON_UNITTEST_TARGET_NAME} PRIVATE "src")
+target_link_libraries(${JSON_UNITTEST_TARGET_NAME} ${JSON_TARGET_NAME})
+
+add_test(NAME "${JSON_UNITTEST_TARGET_NAME}_default"
+	COMMAND ${JSON_UNITTEST_TARGET_NAME}
+	WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}
+)
+add_test(NAME "${JSON_UNITTEST_TARGET_NAME}_all"
+	COMMAND ${JSON_UNITTEST_TARGET_NAME} "*"
+	WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}
+)
diff --git a/test/json.org/1.json b/test/data/json.org/1.json
similarity index 100%
rename from test/json.org/1.json
rename to test/data/json.org/1.json
diff --git a/test/json.org/2.json b/test/data/json.org/2.json
similarity index 100%
rename from test/json.org/2.json
rename to test/data/json.org/2.json
diff --git a/test/json.org/3.json b/test/data/json.org/3.json
similarity index 100%
rename from test/json.org/3.json
rename to test/data/json.org/3.json
diff --git a/test/json.org/4.json b/test/data/json.org/4.json
similarity index 100%
rename from test/json.org/4.json
rename to test/data/json.org/4.json
diff --git a/test/json.org/5.json b/test/data/json.org/5.json
similarity index 100%
rename from test/json.org/5.json
rename to test/data/json.org/5.json
diff --git a/test/json_nlohmann_tests/all_unicode.json b/test/data/json_nlohmann_tests/all_unicode.json
similarity index 100%
rename from test/json_nlohmann_tests/all_unicode.json
rename to test/data/json_nlohmann_tests/all_unicode.json
diff --git a/test/json_nlohmann_tests/bom.json b/test/data/json_nlohmann_tests/bom.json
similarity index 100%
rename from test/json_nlohmann_tests/bom.json
rename to test/data/json_nlohmann_tests/bom.json
diff --git a/test/json_roundtrip/roundtrip01.json b/test/data/json_roundtrip/roundtrip01.json
similarity index 100%
rename from test/json_roundtrip/roundtrip01.json
rename to test/data/json_roundtrip/roundtrip01.json
diff --git a/test/json_roundtrip/roundtrip02.json b/test/data/json_roundtrip/roundtrip02.json
similarity index 100%
rename from test/json_roundtrip/roundtrip02.json
rename to test/data/json_roundtrip/roundtrip02.json
diff --git a/test/json_roundtrip/roundtrip03.json b/test/data/json_roundtrip/roundtrip03.json
similarity index 100%
rename from test/json_roundtrip/roundtrip03.json
rename to test/data/json_roundtrip/roundtrip03.json
diff --git a/test/json_roundtrip/roundtrip04.json b/test/data/json_roundtrip/roundtrip04.json
similarity index 100%
rename from test/json_roundtrip/roundtrip04.json
rename to test/data/json_roundtrip/roundtrip04.json
diff --git a/test/json_roundtrip/roundtrip05.json b/test/data/json_roundtrip/roundtrip05.json
similarity index 100%
rename from test/json_roundtrip/roundtrip05.json
rename to test/data/json_roundtrip/roundtrip05.json
diff --git a/test/json_roundtrip/roundtrip06.json b/test/data/json_roundtrip/roundtrip06.json
similarity index 100%
rename from test/json_roundtrip/roundtrip06.json
rename to test/data/json_roundtrip/roundtrip06.json
diff --git a/test/json_roundtrip/roundtrip07.json b/test/data/json_roundtrip/roundtrip07.json
similarity index 100%
rename from test/json_roundtrip/roundtrip07.json
rename to test/data/json_roundtrip/roundtrip07.json
diff --git a/test/json_roundtrip/roundtrip08.json b/test/data/json_roundtrip/roundtrip08.json
similarity index 100%
rename from test/json_roundtrip/roundtrip08.json
rename to test/data/json_roundtrip/roundtrip08.json
diff --git a/test/json_roundtrip/roundtrip09.json b/test/data/json_roundtrip/roundtrip09.json
similarity index 100%
rename from test/json_roundtrip/roundtrip09.json
rename to test/data/json_roundtrip/roundtrip09.json
diff --git a/test/json_roundtrip/roundtrip10.json b/test/data/json_roundtrip/roundtrip10.json
similarity index 100%
rename from test/json_roundtrip/roundtrip10.json
rename to test/data/json_roundtrip/roundtrip10.json
diff --git a/test/json_roundtrip/roundtrip11.json b/test/data/json_roundtrip/roundtrip11.json
similarity index 100%
rename from test/json_roundtrip/roundtrip11.json
rename to test/data/json_roundtrip/roundtrip11.json
diff --git a/test/json_roundtrip/roundtrip12.json b/test/data/json_roundtrip/roundtrip12.json
similarity index 100%
rename from test/json_roundtrip/roundtrip12.json
rename to test/data/json_roundtrip/roundtrip12.json
diff --git a/test/json_roundtrip/roundtrip13.json b/test/data/json_roundtrip/roundtrip13.json
similarity index 100%
rename from test/json_roundtrip/roundtrip13.json
rename to test/data/json_roundtrip/roundtrip13.json
diff --git a/test/json_roundtrip/roundtrip14.json b/test/data/json_roundtrip/roundtrip14.json
similarity index 100%
rename from test/json_roundtrip/roundtrip14.json
rename to test/data/json_roundtrip/roundtrip14.json
diff --git a/test/json_roundtrip/roundtrip15.json b/test/data/json_roundtrip/roundtrip15.json
similarity index 100%
rename from test/json_roundtrip/roundtrip15.json
rename to test/data/json_roundtrip/roundtrip15.json
diff --git a/test/json_roundtrip/roundtrip16.json b/test/data/json_roundtrip/roundtrip16.json
similarity index 100%
rename from test/json_roundtrip/roundtrip16.json
rename to test/data/json_roundtrip/roundtrip16.json
diff --git a/test/json_roundtrip/roundtrip17.json b/test/data/json_roundtrip/roundtrip17.json
similarity index 100%
rename from test/json_roundtrip/roundtrip17.json
rename to test/data/json_roundtrip/roundtrip17.json
diff --git a/test/json_roundtrip/roundtrip18.json b/test/data/json_roundtrip/roundtrip18.json
similarity index 100%
rename from test/json_roundtrip/roundtrip18.json
rename to test/data/json_roundtrip/roundtrip18.json
diff --git a/test/json_roundtrip/roundtrip19.json b/test/data/json_roundtrip/roundtrip19.json
similarity index 100%
rename from test/json_roundtrip/roundtrip19.json
rename to test/data/json_roundtrip/roundtrip19.json
diff --git a/test/json_roundtrip/roundtrip20.json b/test/data/json_roundtrip/roundtrip20.json
similarity index 100%
rename from test/json_roundtrip/roundtrip20.json
rename to test/data/json_roundtrip/roundtrip20.json
diff --git a/test/json_roundtrip/roundtrip21.json b/test/data/json_roundtrip/roundtrip21.json
similarity index 100%
rename from test/json_roundtrip/roundtrip21.json
rename to test/data/json_roundtrip/roundtrip21.json
diff --git a/test/json_roundtrip/roundtrip22.json b/test/data/json_roundtrip/roundtrip22.json
similarity index 100%
rename from test/json_roundtrip/roundtrip22.json
rename to test/data/json_roundtrip/roundtrip22.json
diff --git a/test/json_roundtrip/roundtrip23.json b/test/data/json_roundtrip/roundtrip23.json
similarity index 100%
rename from test/json_roundtrip/roundtrip23.json
rename to test/data/json_roundtrip/roundtrip23.json
diff --git a/test/json_roundtrip/roundtrip24.json b/test/data/json_roundtrip/roundtrip24.json
similarity index 100%
rename from test/json_roundtrip/roundtrip24.json
rename to test/data/json_roundtrip/roundtrip24.json
diff --git a/test/json_roundtrip/roundtrip25.json b/test/data/json_roundtrip/roundtrip25.json
similarity index 100%
rename from test/json_roundtrip/roundtrip25.json
rename to test/data/json_roundtrip/roundtrip25.json
diff --git a/test/json_roundtrip/roundtrip26.json b/test/data/json_roundtrip/roundtrip26.json
similarity index 100%
rename from test/json_roundtrip/roundtrip26.json
rename to test/data/json_roundtrip/roundtrip26.json
diff --git a/test/json_roundtrip/roundtrip27.json b/test/data/json_roundtrip/roundtrip27.json
similarity index 100%
rename from test/json_roundtrip/roundtrip27.json
rename to test/data/json_roundtrip/roundtrip27.json
diff --git a/test/json_roundtrip/roundtrip28.json b/test/data/json_roundtrip/roundtrip28.json
similarity index 100%
rename from test/json_roundtrip/roundtrip28.json
rename to test/data/json_roundtrip/roundtrip28.json
diff --git a/test/json_roundtrip/roundtrip29.json b/test/data/json_roundtrip/roundtrip29.json
similarity index 100%
rename from test/json_roundtrip/roundtrip29.json
rename to test/data/json_roundtrip/roundtrip29.json
diff --git a/test/json_roundtrip/roundtrip30.json b/test/data/json_roundtrip/roundtrip30.json
similarity index 100%
rename from test/json_roundtrip/roundtrip30.json
rename to test/data/json_roundtrip/roundtrip30.json
diff --git a/test/json_roundtrip/roundtrip31.json b/test/data/json_roundtrip/roundtrip31.json
similarity index 100%
rename from test/json_roundtrip/roundtrip31.json
rename to test/data/json_roundtrip/roundtrip31.json
diff --git a/test/json_roundtrip/roundtrip32.json b/test/data/json_roundtrip/roundtrip32.json
similarity index 100%
rename from test/json_roundtrip/roundtrip32.json
rename to test/data/json_roundtrip/roundtrip32.json
diff --git a/test/json_tests/fail1.json b/test/data/json_tests/fail1.json
similarity index 100%
rename from test/json_tests/fail1.json
rename to test/data/json_tests/fail1.json
diff --git a/test/json_tests/fail10.json b/test/data/json_tests/fail10.json
similarity index 100%
rename from test/json_tests/fail10.json
rename to test/data/json_tests/fail10.json
diff --git a/test/json_tests/fail11.json b/test/data/json_tests/fail11.json
similarity index 100%
rename from test/json_tests/fail11.json
rename to test/data/json_tests/fail11.json
diff --git a/test/json_tests/fail12.json b/test/data/json_tests/fail12.json
similarity index 100%
rename from test/json_tests/fail12.json
rename to test/data/json_tests/fail12.json
diff --git a/test/json_tests/fail13.json b/test/data/json_tests/fail13.json
similarity index 100%
rename from test/json_tests/fail13.json
rename to test/data/json_tests/fail13.json
diff --git a/test/json_tests/fail14.json b/test/data/json_tests/fail14.json
similarity index 100%
rename from test/json_tests/fail14.json
rename to test/data/json_tests/fail14.json
diff --git a/test/json_tests/fail15.json b/test/data/json_tests/fail15.json
similarity index 100%
rename from test/json_tests/fail15.json
rename to test/data/json_tests/fail15.json
diff --git a/test/json_tests/fail16.json b/test/data/json_tests/fail16.json
similarity index 100%
rename from test/json_tests/fail16.json
rename to test/data/json_tests/fail16.json
diff --git a/test/json_tests/fail17.json b/test/data/json_tests/fail17.json
similarity index 100%
rename from test/json_tests/fail17.json
rename to test/data/json_tests/fail17.json
diff --git a/test/json_tests/fail18.json b/test/data/json_tests/fail18.json
similarity index 100%
rename from test/json_tests/fail18.json
rename to test/data/json_tests/fail18.json
diff --git a/test/json_tests/fail19.json b/test/data/json_tests/fail19.json
similarity index 100%
rename from test/json_tests/fail19.json
rename to test/data/json_tests/fail19.json
diff --git a/test/json_tests/fail2.json b/test/data/json_tests/fail2.json
similarity index 100%
rename from test/json_tests/fail2.json
rename to test/data/json_tests/fail2.json
diff --git a/test/json_tests/fail20.json b/test/data/json_tests/fail20.json
similarity index 100%
rename from test/json_tests/fail20.json
rename to test/data/json_tests/fail20.json
diff --git a/test/json_tests/fail21.json b/test/data/json_tests/fail21.json
similarity index 100%
rename from test/json_tests/fail21.json
rename to test/data/json_tests/fail21.json
diff --git a/test/json_tests/fail22.json b/test/data/json_tests/fail22.json
similarity index 100%
rename from test/json_tests/fail22.json
rename to test/data/json_tests/fail22.json
diff --git a/test/json_tests/fail23.json b/test/data/json_tests/fail23.json
similarity index 100%
rename from test/json_tests/fail23.json
rename to test/data/json_tests/fail23.json
diff --git a/test/json_tests/fail24.json b/test/data/json_tests/fail24.json
similarity index 100%
rename from test/json_tests/fail24.json
rename to test/data/json_tests/fail24.json
diff --git a/test/json_tests/fail25.json b/test/data/json_tests/fail25.json
similarity index 100%
rename from test/json_tests/fail25.json
rename to test/data/json_tests/fail25.json
diff --git a/test/json_tests/fail26.json b/test/data/json_tests/fail26.json
similarity index 100%
rename from test/json_tests/fail26.json
rename to test/data/json_tests/fail26.json
diff --git a/test/json_tests/fail27.json b/test/data/json_tests/fail27.json
similarity index 100%
rename from test/json_tests/fail27.json
rename to test/data/json_tests/fail27.json
diff --git a/test/json_tests/fail28.json b/test/data/json_tests/fail28.json
similarity index 100%
rename from test/json_tests/fail28.json
rename to test/data/json_tests/fail28.json
diff --git a/test/json_tests/fail29.json b/test/data/json_tests/fail29.json
similarity index 100%
rename from test/json_tests/fail29.json
rename to test/data/json_tests/fail29.json
diff --git a/test/json_tests/fail3.json b/test/data/json_tests/fail3.json
similarity index 100%
rename from test/json_tests/fail3.json
rename to test/data/json_tests/fail3.json
diff --git a/test/json_tests/fail30.json b/test/data/json_tests/fail30.json
similarity index 100%
rename from test/json_tests/fail30.json
rename to test/data/json_tests/fail30.json
diff --git a/test/json_tests/fail31.json b/test/data/json_tests/fail31.json
similarity index 100%
rename from test/json_tests/fail31.json
rename to test/data/json_tests/fail31.json
diff --git a/test/json_tests/fail32.json b/test/data/json_tests/fail32.json
similarity index 100%
rename from test/json_tests/fail32.json
rename to test/data/json_tests/fail32.json
diff --git a/test/json_tests/fail33.json b/test/data/json_tests/fail33.json
similarity index 100%
rename from test/json_tests/fail33.json
rename to test/data/json_tests/fail33.json
diff --git a/test/json_tests/fail4.json b/test/data/json_tests/fail4.json
similarity index 100%
rename from test/json_tests/fail4.json
rename to test/data/json_tests/fail4.json
diff --git a/test/json_tests/fail5.json b/test/data/json_tests/fail5.json
similarity index 100%
rename from test/json_tests/fail5.json
rename to test/data/json_tests/fail5.json
diff --git a/test/json_tests/fail6.json b/test/data/json_tests/fail6.json
similarity index 100%
rename from test/json_tests/fail6.json
rename to test/data/json_tests/fail6.json
diff --git a/test/json_tests/fail7.json b/test/data/json_tests/fail7.json
similarity index 100%
rename from test/json_tests/fail7.json
rename to test/data/json_tests/fail7.json
diff --git a/test/json_tests/fail8.json b/test/data/json_tests/fail8.json
similarity index 100%
rename from test/json_tests/fail8.json
rename to test/data/json_tests/fail8.json
diff --git a/test/json_tests/fail9.json b/test/data/json_tests/fail9.json
similarity index 100%
rename from test/json_tests/fail9.json
rename to test/data/json_tests/fail9.json
diff --git a/test/json_tests/pass1.json b/test/data/json_tests/pass1.json
similarity index 100%
rename from test/json_tests/pass1.json
rename to test/data/json_tests/pass1.json
diff --git a/test/json_tests/pass2.json b/test/data/json_tests/pass2.json
similarity index 100%
rename from test/json_tests/pass2.json
rename to test/data/json_tests/pass2.json
diff --git a/test/json_tests/pass3.json b/test/data/json_tests/pass3.json
similarity index 100%
rename from test/json_tests/pass3.json
rename to test/data/json_tests/pass3.json
diff --git a/test/json_testsuite/README.md b/test/data/json_testsuite/README.md
similarity index 100%
rename from test/json_testsuite/README.md
rename to test/data/json_testsuite/README.md
diff --git a/test/json_testsuite/sample.json b/test/data/json_testsuite/sample.json
old mode 100755
new mode 100644
similarity index 100%
rename from test/json_testsuite/sample.json
rename to test/data/json_testsuite/sample.json
diff --git a/test/catch.hpp b/test/src/catch.hpp
similarity index 96%
rename from test/catch.hpp
rename to test/src/catch.hpp
index 5cc33a8..879fc5b 100644
--- a/test/catch.hpp
+++ b/test/src/catch.hpp
@@ -1,6 +1,6 @@
 /*
- *  Catch v1.3.4
- *  Generated: 2016-02-10 19:24:03.089683
+ *  Catch v1.5.6
+ *  Generated: 2016-06-09 19:20:41.460328
  *  ----------------------------------------------------------
  *  This file has been merged from multiple headers. Please don't edit it directly
  *  Copyright (c) 2012 Two Blue Cubes Ltd. All rights reserved.
@@ -62,7 +62,11 @@
 
 #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__ )
+#ifdef CATCH_CONFIG_COUNTER
+#  define INTERNAL_CATCH_UNIQUE_NAME( name ) INTERNAL_CATCH_UNIQUE_NAME_LINE( name, __COUNTER__ )
+#else
+#  define INTERNAL_CATCH_UNIQUE_NAME( name ) INTERNAL_CATCH_UNIQUE_NAME_LINE( name, __LINE__ )
+#endif
 
 #define INTERNAL_CATCH_STRINGIFY2( expr ) #expr
 #define INTERNAL_CATCH_STRINGIFY( expr ) INTERNAL_CATCH_STRINGIFY2( expr )
@@ -89,7 +93,7 @@
 // CATCH_CONFIG_CPP11_OR_GREATER : Is C++11 supported?
 
 // CATCH_CONFIG_VARIADIC_MACROS : are variadic macros supported?
-
+// CATCH_CONFIG_COUNTER : is the __COUNTER__ macro supported?
 // ****************
 // Note to maintainers: if new toggles are added please document them
 // in configuration.md, too
@@ -102,6 +106,18 @@
 
 // All the C++11 features can be disabled with CATCH_CONFIG_NO_CPP11
 
+#ifdef __cplusplus
+
+#  if __cplusplus >= 201103L
+#    define CATCH_CPP11_OR_GREATER
+#  endif
+
+#  if __cplusplus >= 201402L
+#    define CATCH_CPP14_OR_GREATER
+#  endif
+
+#endif
+
 #ifdef __clang__
 
 #  if __has_feature(cxx_nullptr)
@@ -112,6 +128,10 @@
 #    define CATCH_INTERNAL_CONFIG_CPP11_NOEXCEPT
 #  endif
 
+#   if defined(CATCH_CPP11_OR_GREATER)
+#       define CATCH_INTERNAL_SUPPRESS_PARENTHESES_WARNINGS _Pragma( "clang diagnostic ignored \"-Wparentheses\"" )
+#   endif
+
 #endif // __clang__
 
 ////////////////////////////////////////////////////////////////////////////////
@@ -136,9 +156,13 @@
 // GCC
 #ifdef __GNUC__
 
-#if __GNUC__ == 4 && __GNUC_MINOR__ >= 6 && defined(__GXX_EXPERIMENTAL_CXX0X__)
-#   define CATCH_INTERNAL_CONFIG_CPP11_NULLPTR
-#endif
+#   if __GNUC__ == 4 && __GNUC_MINOR__ >= 6 && defined(__GXX_EXPERIMENTAL_CXX0X__)
+#       define CATCH_INTERNAL_CONFIG_CPP11_NULLPTR
+#   endif
+
+#   if !defined(CATCH_INTERNAL_SUPPRESS_PARENTHESES_WARNINGS) && defined(CATCH_CPP11_OR_GREATER)
+#       define CATCH_INTERNAL_SUPPRESS_PARENTHESES_WARNINGS _Pragma( "GCC diagnostic ignored \"-Wparentheses\"" )
+#   endif
 
 // - otherwise more recent versions define __cplusplus >= 201103L
 // and will get picked up below
@@ -173,13 +197,20 @@
 
 #endif
 
+// Use __COUNTER__ if the compiler supports it
+#if ( defined _MSC_VER && _MSC_VER >= 1300 ) || \
+    ( defined __GNUC__  && __GNUC__ >= 4 && __GNUC_MINOR__ >= 3 ) || \
+    ( defined __clang__ && __clang_major__ >= 3 )
+
+#define CATCH_INTERNAL_CONFIG_COUNTER
+
+#endif
+
 ////////////////////////////////////////////////////////////////////////////////
 // C++ language feature support
 
 // catch all support for C++11
-#if defined(__cplusplus) && __cplusplus >= 201103L
-
-#  define CATCH_CPP11_OR_GREATER
+#if defined(CATCH_CPP11_OR_GREATER)
 
 #  if !defined(CATCH_INTERNAL_CONFIG_CPP11_NULLPTR)
 #    define CATCH_INTERNAL_CONFIG_CPP11_NULLPTR
@@ -246,6 +277,13 @@
 #if defined(CATCH_INTERNAL_CONFIG_CPP11_UNIQUE_PTR) && !defined(CATCH_CONFIG_NO_UNIQUE_PTR) && !defined(CATCH_CONFIG_CPP11_UNIQUE_PTR) && !defined(CATCH_CONFIG_NO_CPP11)
 #   define CATCH_CONFIG_CPP11_UNIQUE_PTR
 #endif
+#if defined(CATCH_INTERNAL_CONFIG_COUNTER) && !defined(CATCH_CONFIG_NO_COUNTER) && !defined(CATCH_CONFIG_COUNTER)
+#   define CATCH_CONFIG_COUNTER
+#endif
+
+#if !defined(CATCH_INTERNAL_SUPPRESS_PARENTHESES_WARNINGS)
+#   define CATCH_INTERNAL_SUPPRESS_PARENTHESES_WARNINGS
+#endif
 
 // noexcept support:
 #if defined(CATCH_CONFIG_CPP11_NOEXCEPT) && !defined(CATCH_NOEXCEPT)
@@ -672,24 +710,28 @@
 
 #ifdef CATCH_CONFIG_VARIADIC_MACROS
     ///////////////////////////////////////////////////////////////////////////////
+    #define INTERNAL_CATCH_TESTCASE2( TestName, ... ) \
+        static void TestName(); \
+        namespace{ Catch::AutoReg INTERNAL_CATCH_UNIQUE_NAME( autoRegistrar )( &TestName, CATCH_INTERNAL_LINEINFO, Catch::NameAndDesc( __VA_ARGS__ ) ); }\
+        static void TestName()
     #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____ )()
+        INTERNAL_CATCH_TESTCASE2( INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_S_T____ ), __VA_ARGS__ )
 
     ///////////////////////////////////////////////////////////////////////////////
     #define INTERNAL_CATCH_METHOD_AS_TEST_CASE( QualifiedMethod, ... ) \
         namespace{ Catch::AutoReg INTERNAL_CATCH_UNIQUE_NAME( autoRegistrar )( &QualifiedMethod, "&" #QualifiedMethod, Catch::NameAndDesc( __VA_ARGS__ ), CATCH_INTERNAL_LINEINFO ); }
 
     ///////////////////////////////////////////////////////////////////////////////
-    #define INTERNAL_CATCH_TEST_CASE_METHOD( ClassName, ... )\
+    #define INTERNAL_CATCH_TEST_CASE_METHOD2( TestName, ClassName, ... )\
         namespace{ \
-            struct INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_S_T____ ) : ClassName{ \
+            struct TestName : 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 ); \
+            Catch::AutoReg INTERNAL_CATCH_UNIQUE_NAME( autoRegistrar ) ( &TestName::test, #ClassName, Catch::NameAndDesc( __VA_ARGS__ ), CATCH_INTERNAL_LINEINFO ); \
         } \
-        void INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_S_T____ )::test()
+        void TestName::test()
+    #define INTERNAL_CATCH_TEST_CASE_METHOD( ClassName, ... ) \
+        INTERNAL_CATCH_TEST_CASE_METHOD2( INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_S_T____ ), ClassName, __VA_ARGS__ )
 
     ///////////////////////////////////////////////////////////////////////////////
     #define INTERNAL_CATCH_REGISTER_TESTCASE( Function, ... ) \
@@ -697,24 +739,28 @@
 
 #else
     ///////////////////////////////////////////////////////////////////////////////
+    #define INTERNAL_CATCH_TESTCASE2( TestName, Name, Desc ) \
+        static void TestName(); \
+        namespace{ Catch::AutoReg INTERNAL_CATCH_UNIQUE_NAME( autoRegistrar )( &TestName, CATCH_INTERNAL_LINEINFO, Catch::NameAndDesc( Name, Desc ) ); }\
+        static void TestName()
     #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____ )()
+        INTERNAL_CATCH_TESTCASE2( INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_S_T____ ), Name, Desc )
 
     ///////////////////////////////////////////////////////////////////////////////
     #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 )\
+    #define INTERNAL_CATCH_TEST_CASE_METHOD2( TestCaseName, ClassName, TestName, Desc )\
         namespace{ \
-            struct INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_S_T____ ) : ClassName{ \
+            struct TestCaseName : 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 ); \
+            Catch::AutoReg INTERNAL_CATCH_UNIQUE_NAME( autoRegistrar ) ( &TestCaseName::test, #ClassName, Catch::NameAndDesc( TestName, Desc ), CATCH_INTERNAL_LINEINFO ); \
         } \
-        void INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_S_T____ )::test()
+        void TestCaseName::test()
+    #define INTERNAL_CATCH_TEST_CASE_METHOD( ClassName, TestName, Desc )\
+        INTERNAL_CATCH_TEST_CASE_METHOD2( INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_S_T____ ), ClassName, TestName, Desc )
 
     ///////////////////////////////////////////////////////////////////////////////
     #define INTERNAL_CATCH_REGISTER_TESTCASE( Function, Name, Desc ) \
@@ -1287,37 +1333,37 @@
     template<typename T1, typename T2>
     struct Evaluator<T1, T2, IsEqualTo> {
         static bool evaluate( T1 const& lhs, T2 const& rhs) {
-            return opCast( lhs ) ==  opCast( rhs );
+            return bool( opCast( lhs ) ==  opCast( rhs ) );
         }
     };
     template<typename T1, typename T2>
     struct Evaluator<T1, T2, IsNotEqualTo> {
         static bool evaluate( T1 const& lhs, T2 const& rhs ) {
-            return opCast( lhs ) != opCast( rhs );
+            return bool( opCast( lhs ) != opCast( rhs ) );
         }
     };
     template<typename T1, typename T2>
     struct Evaluator<T1, T2, IsLessThan> {
         static bool evaluate( T1 const& lhs, T2 const& rhs ) {
-            return opCast( lhs ) < opCast( rhs );
+            return bool( opCast( lhs ) < opCast( rhs ) );
         }
     };
     template<typename T1, typename T2>
     struct Evaluator<T1, T2, IsGreaterThan> {
         static bool evaluate( T1 const& lhs, T2 const& rhs ) {
-            return opCast( lhs ) > opCast( rhs );
+            return bool( opCast( lhs ) > opCast( rhs ) );
         }
     };
     template<typename T1, typename T2>
     struct Evaluator<T1, T2, IsGreaterThanOrEqualTo> {
         static bool evaluate( T1 const& lhs, T2 const& rhs ) {
-            return opCast( lhs ) >= opCast( rhs );
+            return bool( opCast( lhs ) >= opCast( rhs ) );
         }
     };
     template<typename T1, typename T2>
     struct Evaluator<T1, T2, IsLessThanOrEqualTo> {
         static bool evaluate( T1 const& lhs, T2 const& rhs ) {
-            return opCast( lhs ) <= opCast( rhs );
+            return bool( opCast( lhs ) <= opCast( rhs ) );
         }
     };
 
@@ -2020,13 +2066,14 @@
     do { \
         Catch::ResultBuilder __catchResult( macroName, CATCH_INTERNAL_LINEINFO, #expr, resultDisposition ); \
         try { \
+            CATCH_INTERNAL_SUPPRESS_PARENTHESES_WARNINGS \
             ( __catchResult <= expr ).endExpression(); \
         } \
         catch( ... ) { \
             __catchResult.useActiveException( Catch::ResultDisposition::Normal ); \
         } \
         INTERNAL_CATCH_REACT( __catchResult ) \
-    } while( Catch::isTrue( false && static_cast<bool>(expr) ) ) // expr here is never evaluated at runtime but it forces the compiler to give it a look
+    } 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 ) \
@@ -2563,10 +2610,12 @@
 }
 
 ///////////////////////////////////////////////////////////////////////////////
-#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 )
+#define INTERNAL_CATCH_TRANSLATE_EXCEPTION2( translatorName, signature ) \
+    static std::string translatorName( signature ); \
+    namespace{ Catch::ExceptionTranslatorRegistrar INTERNAL_CATCH_UNIQUE_NAME( catch_internal_ExceptionRegistrar )( &translatorName ); }\
+    static std::string translatorName( signature )
+
+#define INTERNAL_CATCH_TRANSLATE_EXCEPTION( signature ) INTERNAL_CATCH_TRANSLATE_EXCEPTION2( INTERNAL_CATCH_UNIQUE_NAME( catch_internal_ExceptionTranslator ), signature )
 
 // #included from: internal/catch_approx.hpp
 #define TWOBLUECUBES_CATCH_APPROX_HPP_INCLUDED
@@ -3331,6 +3380,11 @@
         InLexicographicalOrder,
         InRandomOrder
     }; };
+    struct UseColour { enum YesOrNo {
+        Auto,
+        Yes,
+        No
+    }; };
 
     class TestSpec;
 
@@ -3350,7 +3404,7 @@
         virtual TestSpec const& testSpec() const = 0;
         virtual RunTests::InWhatOrder runOrder() const = 0;
         virtual unsigned int rngSeed() const = 0;
-        virtual bool forceColour() const = 0;
+        virtual UseColour::YesOrNo useColour() const = 0;
     };
 }
 
@@ -3404,7 +3458,7 @@
     };
 
     class DebugOutStream : public IStream {
-        std::auto_ptr<StreamBufBase> m_streamBuf;
+        CATCH_AUTO_PTR( StreamBufBase ) m_streamBuf;
         mutable std::ostream m_os;
     public:
         DebugOutStream();
@@ -3439,14 +3493,14 @@
             noThrow( false ),
             showHelp( false ),
             showInvisibles( false ),
-            forceColour( false ),
             filenamesAsTags( false ),
             abortAfter( -1 ),
             rngSeed( 0 ),
             verbosity( Verbosity::Normal ),
             warnings( WarnAbout::Nothing ),
             showDurations( ShowDurations::DefaultForReporter ),
-            runOrder( RunTests::InDeclarationOrder )
+            runOrder( RunTests::InDeclarationOrder ),
+            useColour( UseColour::Auto )
         {}
 
         bool listTests;
@@ -3459,7 +3513,6 @@
         bool noThrow;
         bool showHelp;
         bool showInvisibles;
-        bool forceColour;
         bool filenamesAsTags;
 
         int abortAfter;
@@ -3469,6 +3522,7 @@
         WarnAbout::What warnings;
         ShowDurations::OrNot showDurations;
         RunTests::InWhatOrder runOrder;
+        UseColour::YesOrNo useColour;
 
         std::string outputFilename;
         std::string name;
@@ -3534,7 +3588,7 @@
         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; }
+        virtual UseColour::YesOrNo useColour() const { return m_data.useColour; }
 
     private:
 
@@ -3552,7 +3606,7 @@
         }
         ConfigData m_data;
 
-        std::auto_ptr<IStream const> m_stream;
+        CATCH_AUTO_PTR( IStream const ) m_stream;
         TestSpec m_testSpec;
     };
 
@@ -3572,7 +3626,7 @@
 #define STITCH_CLARA_OPEN_NAMESPACE namespace Catch {
 // #included from: ../external/clara.h
 
-// Version 0.0.1.1
+// Version 0.0.2.4
 
 // Only use header guard if we are not using an outer namespace
 #if !defined(TWOBLUECUBES_CLARA_H_INCLUDED) || defined(STITCH_CLARA_OPEN_NAMESPACE)
@@ -3888,6 +3942,10 @@
 #include <stdexcept>
 #include <memory>
 
+#if defined(WIN32) || defined(__WIN32__) || defined(_WIN32) || defined(_MSC_VER)
+#define CLARA_PLATFORM_WINDOWS
+#endif
+
 // Use optional outer namespace
 #ifdef STITCH_CLARA_OPEN_NAMESPACE
 STITCH_CLARA_OPEN_NAMESPACE
@@ -3911,9 +3969,6 @@
     const unsigned int consoleWidth = 80;
 #endif
 
-        // Use this to try and stop compiler from warning about unreachable code
-        inline bool isTrue( bool value ) { return value; }
-
         using namespace Tbc;
 
         inline bool startsWith( std::string const& str, std::string const& prefix ) {
@@ -3949,14 +4004,6 @@
             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& ) {
-            if( isTrue( true ) )
-                throw std::runtime_error( "Invalid conversion" );
-        }
 
         template<typename ConfigT>
         struct IArgFunction {
@@ -3966,7 +4013,6 @@
             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;
         };
@@ -3988,9 +4034,6 @@
             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 {
@@ -4003,7 +4046,6 @@
         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 ); }
         };
@@ -4014,9 +4056,6 @@
             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;
@@ -4029,11 +4068,6 @@
                 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 );
@@ -4047,9 +4081,6 @@
                 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)();
@@ -4064,9 +4095,6 @@
                 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& );
@@ -4080,11 +4108,6 @@
                 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 );
@@ -4092,8 +4115,20 @@
 
     } // namespace Detail
 
-    struct Parser {
-        Parser() : separators( " \t=:" ) {}
+    inline std::vector<std::string> argsToVector( int argc, char const* const* const argv ) {
+        std::vector<std::string> args( static_cast<std::size_t>( argc ) );
+        for( std::size_t i = 0; i < static_cast<std::size_t>( argc ); ++i )
+            args[i] = argv[i];
+
+        return args;
+    }
+
+    class Parser {
+        enum Mode { None, MaybeShortOpt, SlashOpt, ShortOpt, LongOpt, Positional };
+        Mode mode;
+        std::size_t from;
+        bool inQuotes;
+    public:
 
         struct Token {
             enum Type { Positional, ShortOpt, LongOpt };
@@ -4102,38 +4137,75 @@
             std::string data;
         };
 
-        void parseIntoTokens( int argc, char const* const argv[], std::vector<Parser::Token>& tokens ) const {
+        Parser() : mode( None ), from( 0 ), inQuotes( false ){}
+
+        void parseIntoTokens( std::vector<std::string> const& args, std::vector<Token>& tokens ) {
             const std::string doubleDash = "--";
-            for( int i = 1; i < argc && argv[i] != doubleDash; ++i )
-                parseIntoTokens( argv[i] , tokens);
+            for( std::size_t i = 1; i < args.size() && args[i] != doubleDash; ++i )
+                parseIntoTokens( args[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 );
+
+        void parseIntoTokens( std::string const& arg, std::vector<Token>& tokens ) {
+            for( std::size_t i = 0; i <= arg.size(); ++i ) {
+                char c = arg[i];
+                if( c == '"' )
+                    inQuotes = !inQuotes;
+                mode = handleMode( i, c, arg, tokens );
             }
         }
-        std::string separators;
+        Mode handleMode( std::size_t i, char c, std::string const& arg, std::vector<Token>& tokens ) {
+            switch( mode ) {
+                case None: return handleNone( i, c );
+                case MaybeShortOpt: return handleMaybeShortOpt( i, c );
+                case ShortOpt:
+                case LongOpt:
+                case SlashOpt: return handleOpt( i, c, arg, tokens );
+                case Positional: return handlePositional( i, c, arg, tokens );
+                default: throw std::logic_error( "Unknown mode" );
+            }
+        }
+
+        Mode handleNone( std::size_t i, char c ) {
+            if( inQuotes ) {
+                from = i;
+                return Positional;
+            }
+            switch( c ) {
+                case '-': return MaybeShortOpt;
+#ifdef CLARA_PLATFORM_WINDOWS
+                case '/': from = i+1; return SlashOpt;
+#endif
+                default: from = i; return Positional;
+            }
+        }
+        Mode handleMaybeShortOpt( std::size_t i, char c ) {
+            switch( c ) {
+                case '-': from = i+1; return LongOpt;
+                default: from = i; return ShortOpt;
+            }
+        }
+        Mode handleOpt( std::size_t i, char c, std::string const& arg, std::vector<Token>& tokens ) {
+            if( std::string( ":=\0", 3 ).find( c ) == std::string::npos )
+                return mode;
+
+            std::string optName = arg.substr( from, i-from );
+            if( mode == ShortOpt )
+                for( std::size_t j = 0; j < optName.size(); ++j )
+                    tokens.push_back( Token( Token::ShortOpt, optName.substr( j, 1 ) ) );
+            else if( mode == SlashOpt && optName.size() == 1 )
+                tokens.push_back( Token( Token::ShortOpt, optName ) );
+            else
+                tokens.push_back( Token( Token::LongOpt, optName ) );
+            return None;
+        }
+        Mode handlePositional( std::size_t i, char c, std::string const& arg, std::vector<Token>& tokens ) {
+            if( inQuotes || std::string( "\0", 1 ).find( c ) == std::string::npos )
+                return mode;
+
+            std::string data = arg.substr( from, i-from );
+            tokens.push_back( Token( Token::Positional, data ) );
+            return None;
+        }
     };
 
     template<typename ConfigT>
@@ -4436,21 +4508,21 @@
             return oss.str();
         }
 
-        ConfigT parse( int argc, char const* const argv[] ) const {
+        ConfigT parse( std::vector<std::string> const& args ) const {
             ConfigT config;
-            parseInto( argc, argv, config );
+            parseInto( args, config );
             return config;
         }
 
-        std::vector<Parser::Token> parseInto( int argc, char const* argv[], ConfigT& config ) const {
-            std::string processName = argv[0];
+        std::vector<Parser::Token> parseInto( std::vector<std::string> const& args, ConfigT& config ) const {
+            std::string processName = args[0];
             std::size_t lastSlash = processName.find_last_of( "/\\" );
             if( lastSlash != std::string::npos )
                 processName = processName.substr( lastSlash+1 );
             m_boundProcessName.set( config, processName );
             std::vector<Parser::Token> tokens;
             Parser parser;
-            parser.parseIntoTokens( argc, argv, tokens );
+            parser.parseIntoTokens( args, tokens );
             return populate( tokens, config );
         }
 
@@ -4481,7 +4553,7 @@
                                     arg.boundField.set( config, tokens[++i].data );
                             }
                             else {
-                                arg.boundField.setFlag( config );
+                                arg.boundField.set( config, "true" );
                             }
                             break;
                         }
@@ -4624,6 +4696,21 @@
             ? ShowDurations::Always
             : ShowDurations::Never;
     }
+    inline void setUseColour( ConfigData& config, std::string const& value ) {
+        std::string mode = toLower( value );
+
+        if( mode == "yes" )
+            config.useColour = UseColour::Yes;
+        else if( mode == "no" )
+            config.useColour = UseColour::No;
+        else if( mode == "auto" )
+            config.useColour = UseColour::Auto;
+        else
+            throw std::runtime_error( "colour mode must be one of: auto, yes or no" );
+    }
+    inline void forceColour( ConfigData& config ) {
+        config.useColour = UseColour::Yes;
+    }
     inline void loadTestNamesFromFile( ConfigData& config, std::string const& _filename ) {
         std::ifstream f( _filename.c_str() );
         if( !f.is_open() )
@@ -4710,7 +4797,7 @@
 
         cli["-d"]["--durations"]
             .describe( "show test durations" )
-            .bind( &setShowDurations, "yes/no" );
+            .bind( &setShowDurations, "yes|no" );
 
         cli["-f"]["--input-file"]
             .describe( "load test names to run from a file" )
@@ -4738,8 +4825,12 @@
             .bind( &setRngSeed, "'time'|number" );
 
         cli["--force-colour"]
-            .describe( "force colourised output" )
-            .bind( &ConfigData::forceColour );
+            .describe( "force colourised output (deprecated)" )
+            .bind( &forceColour );
+
+        cli["--use-colour"]
+            .describe( "should output be colourised" )
+            .bind( &setUseColour, "yes|no" );
 
         return cli;
     }
@@ -5170,6 +5261,8 @@
         bool aborting;
     };
 
+    class MultipleReporters;
+
     struct IStreamingReporter : IShared {
         virtual ~IStreamingReporter();
 
@@ -5197,6 +5290,8 @@
         virtual void testRunEnded( TestRunStats const& testRunStats ) = 0;
 
         virtual void skipTest( TestCaseInfo const& testInfo ) = 0;
+
+        virtual MultipleReporters* tryAsMulti() { return CATCH_NULL; }
     };
 
     struct IReporterFactory : IShared {
@@ -5414,6 +5509,10 @@
         virtual void addChild( Ptr<ITracker> const& child ) = 0;
         virtual ITracker* findChild( std::string const& name ) = 0;
         virtual void openChild() = 0;
+
+        // Debug/ checking
+        virtual bool isSectionTracker() const = 0;
+        virtual bool isIndexTracker() const = 0;
     };
 
     class TrackerContext {
@@ -5538,6 +5637,10 @@
                     m_parent->openChild();
             }
         }
+
+        virtual bool isSectionTracker() const CATCH_OVERRIDE { return false; }
+        virtual bool isIndexTracker() const CATCH_OVERRIDE { return false; }
+
         void open() {
             m_runState = Executing;
             moveToThis();
@@ -5601,13 +5704,16 @@
         {}
         virtual ~SectionTracker();
 
+        virtual bool isSectionTracker() const CATCH_OVERRIDE { return true; }
+
         static SectionTracker& acquire( TrackerContext& ctx, std::string const& name ) {
             SectionTracker* section = CATCH_NULL;
 
             ITracker& currentTracker = ctx.currentTracker();
             if( ITracker* childTracker = currentTracker.findChild( name ) ) {
-                section = dynamic_cast<SectionTracker*>( childTracker );
-                assert( section );
+                assert( childTracker );
+                assert( childTracker->isSectionTracker() );
+                section = static_cast<SectionTracker*>( childTracker );
             }
             else {
                 section = new SectionTracker( name, ctx, &currentTracker );
@@ -5632,13 +5738,16 @@
         {}
         virtual ~IndexTracker();
 
+        virtual bool isIndexTracker() const CATCH_OVERRIDE { return true; }
+
         static IndexTracker& acquire( TrackerContext& ctx, std::string const& name, int size ) {
             IndexTracker* tracker = CATCH_NULL;
 
             ITracker& currentTracker = ctx.currentTracker();
             if( ITracker* childTracker = currentTracker.findChild( name ) ) {
-                tracker = dynamic_cast<IndexTracker*>( childTracker );
-                assert( tracker );
+                assert( childTracker );
+                assert( childTracker->isIndexTracker() );
+                tracker = static_cast<IndexTracker*>( childTracker );
             }
             else {
                 tracker = new IndexTracker( name, ctx, &currentTracker, size );
@@ -5845,6 +5954,11 @@
             while( getCurrentContext().advanceGeneratorsForCurrentTest() && !aborting() );
 
             Totals deltaTotals = m_totals.delta( prevTotals );
+            if( testInfo.expectedToFail() && deltaTotals.testCases.passed > 0 ) {
+                deltaTotals.assertions.failed++;
+                deltaTotals.testCases.passed--;
+                deltaTotals.testCases.failed++;
+            }
             m_totals.testCases += deltaTotals.testCases;
             m_reporter->testCaseEnded( TestCaseStats(   testInfo,
                                                         deltaTotals,
@@ -6236,10 +6350,10 @@
             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 ) {
+        int applyCommandLine( int argc, char const* const* const argv, OnUnusedOptions::DoWhat unusedOptionBehaviour = OnUnusedOptions::Fail ) {
             try {
                 m_cli.setThrowOnUnrecognisedTokens( unusedOptionBehaviour == OnUnusedOptions::Fail );
-                m_unusedTokens = m_cli.parseInto( argc, argv, m_configData );
+                m_unusedTokens = m_cli.parseInto( Clara::argsToVector( argc, argv ), m_configData );
                 if( m_configData.showHelp )
                     showHelp( m_configData.processName );
                 m_config.reset();
@@ -6263,16 +6377,13 @@
             m_config.reset();
         }
 
-        int run( int argc, char const* argv[] ) {
+        int run( int argc, char const* const* const argv ) {
 
             int returnCode = applyCommandLine( argc, argv );
             if( returnCode == 0 )
                 returnCode = run();
             return returnCode;
         }
-        int run( int argc, char* argv[] ) {
-            return run( argc, const_cast<char const**>( argv ) );
-        }
 
         int run() {
             if( m_configData.showHelp )
@@ -6336,13 +6447,31 @@
 #include <iostream>
 #include <algorithm>
 
+#ifdef CATCH_CPP14_OR_GREATER
+#include <random>
+#endif
+
 namespace Catch {
 
-    struct LexSort {
-        bool operator() (TestCase i,TestCase j) const { return (i<j);}
-    };
     struct RandomNumberGenerator {
-        int operator()( int n ) const { return std::rand() % n; }
+        typedef int result_type;
+
+        result_type operator()( result_type n ) const { return std::rand() % n; }
+
+#ifdef CATCH_CPP14_OR_GREATER
+        static constexpr result_type min() { return 0; }
+        static constexpr result_type max() { return 1000000; }
+        result_type operator()() const { return std::rand() % max(); }
+#endif
+        template<typename V>
+        static void shuffle( V& vector ) {
+            RandomNumberGenerator rng;
+#ifdef CATCH_CPP14_OR_GREATER
+            std::shuffle( vector.begin(), vector.end(), rng );
+#else
+            std::random_shuffle( vector.begin(), vector.end(), rng );
+#endif
+        }
     };
 
     inline std::vector<TestCase> sortTests( IConfig const& config, std::vector<TestCase> const& unsortedTestCases ) {
@@ -6351,14 +6480,12 @@
 
         switch( config.runOrder() ) {
             case RunTests::InLexicographicalOrder:
-                std::sort( sorted.begin(), sorted.end(), LexSort() );
+                std::sort( sorted.begin(), sorted.end() );
                 break;
             case RunTests::InRandomOrder:
                 {
                     seedRng( config );
-
-                    RandomNumberGenerator rng;
-                    std::random_shuffle( sorted.begin(), sorted.end(), rng );
+                    RandomNumberGenerator::shuffle( sorted );
                 }
                 break;
             case RunTests::InDeclarationOrder:
@@ -6377,13 +6504,15 @@
             it != itEnd;
             ++it ) {
             std::pair<std::set<TestCase>::const_iterator, bool> prev = seenFunctions.insert( *it );
-            if( !prev.second ){
-                Catch::cerr()
-                << Colour( Colour::Red )
-                << "error: TEST_CASE( \"" << it->name << "\" ) already defined.\n"
-                << "\tFirst seen at " << prev.first->getTestCaseInfo().lineInfo << "\n"
-                << "\tRedefined at " << it->getTestCaseInfo().lineInfo << std::endl;
-                exit(1);
+            if( !prev.second ) {
+                std::ostringstream ss;
+
+                ss  << Colour( Colour::Red )
+                    << "error: TEST_CASE( \"" << it->name << "\" ) already defined.\n"
+                    << "\tFirst seen at " << prev.first->getTestCaseInfo().lineInfo << "\n"
+                    << "\tRedefined at " << it->getTestCaseInfo().lineInfo << std::endl;
+
+                throw std::runtime_error(ss.str());
             }
         }
     }
@@ -6971,7 +7100,18 @@
 
     IColourImpl* platformColourInstance() {
         static Win32ColourImpl s_instance;
-        return &s_instance;
+
+        Ptr<IConfig const> config = getCurrentContext().getConfig();
+        UseColour::YesOrNo colourMode = config
+            ? config->useColour()
+            : UseColour::Auto;
+        if( colourMode == UseColour::Auto )
+            colourMode = !isDebuggerActive()
+                ? UseColour::Yes
+                : UseColour::No;
+        return colourMode == UseColour::Yes
+            ? &s_instance
+            : NoColourImpl::instance();
     }
 
 } // end anon namespace
@@ -7022,7 +7162,14 @@
 
     IColourImpl* platformColourInstance() {
         Ptr<IConfig const> config = getCurrentContext().getConfig();
-        return (config && config->forceColour()) || isatty(STDOUT_FILENO)
+        UseColour::YesOrNo colourMode = config
+            ? config->useColour()
+            : UseColour::Auto;
+        if( colourMode == UseColour::Auto )
+            colourMode = (!isDebuggerActive() && isatty(STDOUT_FILENO) )
+                ? UseColour::Yes
+                : UseColour::No;
+        return colourMode == UseColour::Yes
             ? PosixColourImpl::instance()
             : NoColourImpl::instance();
     }
@@ -7047,9 +7194,7 @@
     Colour::~Colour(){ if( !m_moved ) use( None ); }
 
     void Colour::use( Code _colourCode ) {
-        static IColourImpl* impl = isDebuggerActive()
-            ? NoColourImpl::instance()
-            : platformColourInstance();
+        static IColourImpl* impl = platformColourInstance();
         impl->use( _colourCode );
     }
 
@@ -7426,7 +7571,7 @@
         return os;
     }
 
-    Version libraryVersion( 1, 3, 4, "", 0 );
+    Version libraryVersion( 1, 5, 6, "", 0 );
 
 }
 
@@ -8405,13 +8550,18 @@
                 ++it )
             (*it)->skipTest( testInfo );
     }
+
+    virtual MultipleReporters* tryAsMulti() CATCH_OVERRIDE {
+        return this;
+    }
+
 };
 
 Ptr<IStreamingReporter> addReporter( Ptr<IStreamingReporter> const& existingReporter, Ptr<IStreamingReporter> const& additionalReporter ) {
     Ptr<IStreamingReporter> resultingReporter;
 
     if( existingReporter ) {
-        MultipleReporters* multi = dynamic_cast<MultipleReporters*>( existingReporter.get() );
+        MultipleReporters* multi = existingReporter->tryAsMulti();
         if( !multi ) {
             multi = new MultipleReporters;
             resultingReporter = Ptr<IStreamingReporter>( multi );
@@ -8591,7 +8741,7 @@
 
         virtual void assertionStarting( AssertionInfo const& ) CATCH_OVERRIDE {}
 
-        virtual bool assertionEnded( AssertionStats const& assertionStats ) {
+        virtual bool assertionEnded( AssertionStats const& assertionStats ) CATCH_OVERRIDE {
             assert( !m_sectionStack.empty() );
             SectionNode& sectionNode = *m_sectionStack.back();
             sectionNode.assertions.push_back( assertionStats );
@@ -9722,7 +9872,7 @@
             if( totals.testCases.total() == 0 ) {
                 stream << Colour( Colour::Warning ) << "No tests ran\n";
             }
-            else if( totals.assertions.total() > 0 && totals.assertions.allPassed() ) {
+            else if( totals.assertions.total() > 0 && totals.testCases.allPassed() ) {
                 stream << Colour( Colour::ResultSuccess ) << "All tests passed";
                 stream << " ("
                         << pluralise( totals.assertions.passed, "assertion" ) << " in "
diff --git a/test/fuzz.cpp b/test/src/fuzz.cpp
similarity index 100%
rename from test/fuzz.cpp
rename to test/src/fuzz.cpp
diff --git a/test/unit.cpp b/test/src/unit.cpp
similarity index 87%
rename from test/unit.cpp
rename to test/src/unit.cpp
index ab96364..298a2bb 100644
--- a/test/unit.cpp
+++ b/test/src/unit.cpp
@@ -1285,7 +1285,7 @@
 
     SECTION("create a JSON value from an input stream")
     {
-        SECTION("sts::stringstream")
+        SECTION("std::stringstream")
         {
             std::stringstream ss;
             ss << "[\"foo\",1,2,3,false,{\"one\":1}]";
@@ -1314,7 +1314,7 @@
 
         SECTION("std::ifstream")
         {
-            std::ifstream f("test/json_tests/pass1.json");
+            std::ifstream f("test/data/json_tests/pass1.json");
             json j(f);
         }
     }
@@ -3455,7 +3455,7 @@
                 {
                     json jarray = {1, 1u, true, nullptr, "string", 42.23, json::object(), {1, 2, 3}};
                     CHECK_THROWS_AS(jarray.erase(8), std::out_of_range);
-                    CHECK_THROWS_WITH(jarray.erase(8), "index out of range");
+                    CHECK_THROWS_WITH(jarray.erase(8), "array index 8 is out of range");
                 }
             }
 
@@ -7920,6 +7920,42 @@
                                   "cannot use push_back() with number");
             }
         }
+
+        SECTION("with initializer_list")
+        {
+            SECTION("null")
+            {
+                json j;
+                j.push_back({"foo", "bar"});
+                CHECK(j == json::array({{"foo", "bar"}}));
+
+                json k;
+                k.push_back({1, 2, 3});
+                CHECK(k == json::array({{1, 2, 3}}));
+            }
+
+            SECTION("array")
+            {
+                json j = {1, 2, 3};
+                j.push_back({"foo", "bar"});
+                CHECK(j == json({1, 2, 3, {"foo", "bar"}}));
+
+                json k = {1, 2, 3};
+                k.push_back({1, 2, 3});
+                CHECK(k == json({1, 2, 3, {1, 2, 3}}));
+            }
+
+            SECTION("object")
+            {
+                json j = {{"key1", 1}};
+                j.push_back({"key2", "bar"});
+                CHECK(j == json({{"key1", 1}, {"key2", "bar"}}));
+
+                json k = {{"key1", 1}};
+                CHECK_THROWS_AS(k.push_back({1, 2, 3, 4}), std::domain_error);
+                CHECK_THROWS_WITH(k.push_back({1, 2, 3, 4}), "cannot use push_back() with object");
+            }
+        }
     }
 
     SECTION("operator+=")
@@ -8016,6 +8052,42 @@
                                   "cannot use push_back() with number");
             }
         }
+
+        SECTION("with initializer_list")
+        {
+            SECTION("null")
+            {
+                json j;
+                j += {"foo", "bar"};
+                CHECK(j == json::array({{"foo", "bar"}}));
+
+                json k;
+                k += {1, 2, 3};
+                CHECK(k == json::array({{1, 2, 3}}));
+            }
+
+            SECTION("array")
+            {
+                json j = {1, 2, 3};
+                j += {"foo", "bar"};
+                CHECK(j == json({1, 2, 3, {"foo", "bar"}}));
+
+                json k = {1, 2, 3};
+                k += {1, 2, 3};
+                CHECK(k == json({1, 2, 3, {1, 2, 3}}));
+            }
+
+            SECTION("object")
+            {
+                json j = {{"key1", 1}};
+                j += {"key2", "bar"};
+                CHECK(j == json({{"key1", 1}, {"key2", "bar"}}));
+
+                json k = {{"key1", 1}};
+                CHECK_THROWS_AS((k += {1, 2, 3, 4}), std::domain_error);
+                CHECK_THROWS_WITH((k += {1, 2, 3, 4}), "cannot use push_back() with object");
+            }
+        }
     }
 
     SECTION("insert")
@@ -9776,6 +9848,10 @@
                 CHECK_THROWS_AS(json::parser("-0e-:").parse(), std::invalid_argument);
                 CHECK_THROWS_AS(json::parser("-0f").parse(), std::invalid_argument);
 
+                // numbers must not begin with "+"
+                CHECK_THROWS_AS(json::parser("+1").parse(), std::invalid_argument);
+                CHECK_THROWS_AS(json::parser("+0").parse(), std::invalid_argument);
+
                 CHECK_THROWS_WITH(json::parser("01").parse(),
                                   "parse error - unexpected number literal; expected end of input");
                 CHECK_THROWS_WITH(json::parser("--1").parse(), "parse error - unexpected '-'");
@@ -10295,7 +10371,7 @@
         // create object from string literal
         json j = "{ \"happy\": true, \"pi\": 3.141 }"_json;
 
-        // or even nicer (thanks http://isocpp.org/blog/2015/01/json-for-modern-cpp)
+        // or even nicer with a raw string literal
         auto j2 = R"(
           {
             "happy": true,
@@ -10423,7 +10499,7 @@
     }
 
     {
-        /// strings
+        // strings
         std::string s1 = "Hello, world!";
         json js = s1;
         std::string s2 = js;
@@ -10446,6 +10522,40 @@
 
         // etc.
     }
+
+    {
+        // a JSON value
+        json j_original = R"({
+          "baz": ["one", "two", "three"],
+          "foo": "bar"
+        })"_json;
+
+        // access members with a JSON pointer (RFC 6901)
+        j_original["/baz/2"_json_pointer];
+        // "two"
+
+        // a JSON patch (RFC 6902)
+        json j_patch = R"([
+          { "op": "replace", "path": "/baz", "value": "boo" },
+          { "op": "add", "path": "/hello", "value": ["world"] },
+          { "op": "remove", "path": "/foo"}
+        ])"_json;
+
+        // apply the patch
+        json j_result = j_original.patch(j_patch);
+        // {
+        //    "baz": "boo",
+        //    "hello": ["world"]
+        // }
+
+        // calculate a JSON patch from two JSON values
+        json::diff(j_result, j_original);
+        // [
+        //   { "op":" replace", "path": "/baz", "value": ["one", "two", "three"] },
+        //   { "op":"remove","path":"/hello" },
+        //   { "op":"add","path":"/foo","value":"bar" }
+        // ]
+    }
 }
 
 TEST_CASE("algorithms")
@@ -11576,39 +11686,39 @@
     {
         for (auto filename :
                 {
-                    //"test/json_tests/fail1.json",
-                    "test/json_tests/fail2.json",
-                    "test/json_tests/fail3.json",
-                    "test/json_tests/fail4.json",
-                    "test/json_tests/fail5.json",
-                    "test/json_tests/fail6.json",
-                    "test/json_tests/fail7.json",
-                    "test/json_tests/fail8.json",
-                    "test/json_tests/fail9.json",
-                    "test/json_tests/fail10.json",
-                    "test/json_tests/fail11.json",
-                    "test/json_tests/fail12.json",
-                    "test/json_tests/fail13.json",
-                    "test/json_tests/fail14.json",
-                    "test/json_tests/fail15.json",
-                    "test/json_tests/fail16.json",
-                    "test/json_tests/fail17.json",
-                    //"test/json_tests/fail18.json",
-                    "test/json_tests/fail19.json",
-                    "test/json_tests/fail20.json",
-                    "test/json_tests/fail21.json",
-                    "test/json_tests/fail22.json",
-                    "test/json_tests/fail23.json",
-                    "test/json_tests/fail24.json",
-                    "test/json_tests/fail25.json",
-                    "test/json_tests/fail26.json",
-                    "test/json_tests/fail27.json",
-                    "test/json_tests/fail28.json",
-                    "test/json_tests/fail29.json",
-                    "test/json_tests/fail30.json",
-                    "test/json_tests/fail31.json",
-                    "test/json_tests/fail32.json",
-                    "test/json_tests/fail33.json"
+                    //"test/data/json_tests/fail1.json",
+                    "test/data/json_tests/fail2.json",
+                    "test/data/json_tests/fail3.json",
+                    "test/data/json_tests/fail4.json",
+                    "test/data/json_tests/fail5.json",
+                    "test/data/json_tests/fail6.json",
+                    "test/data/json_tests/fail7.json",
+                    "test/data/json_tests/fail8.json",
+                    "test/data/json_tests/fail9.json",
+                    "test/data/json_tests/fail10.json",
+                    "test/data/json_tests/fail11.json",
+                    "test/data/json_tests/fail12.json",
+                    "test/data/json_tests/fail13.json",
+                    "test/data/json_tests/fail14.json",
+                    "test/data/json_tests/fail15.json",
+                    "test/data/json_tests/fail16.json",
+                    "test/data/json_tests/fail17.json",
+                    //"test/data/json_tests/fail18.json",
+                    "test/data/json_tests/fail19.json",
+                    "test/data/json_tests/fail20.json",
+                    "test/data/json_tests/fail21.json",
+                    "test/data/json_tests/fail22.json",
+                    "test/data/json_tests/fail23.json",
+                    "test/data/json_tests/fail24.json",
+                    "test/data/json_tests/fail25.json",
+                    "test/data/json_tests/fail26.json",
+                    "test/data/json_tests/fail27.json",
+                    "test/data/json_tests/fail28.json",
+                    "test/data/json_tests/fail29.json",
+                    "test/data/json_tests/fail30.json",
+                    "test/data/json_tests/fail31.json",
+                    "test/data/json_tests/fail32.json",
+                    "test/data/json_tests/fail33.json"
                 })
         {
             CAPTURE(filename);
@@ -11622,9 +11732,9 @@
     {
         for (auto filename :
                 {
-                    "test/json_tests/pass1.json",
-                    "test/json_tests/pass2.json",
-                    "test/json_tests/pass3.json"
+                    "test/data/json_tests/pass1.json",
+                    "test/data/json_tests/pass2.json",
+                    "test/data/json_tests/pass3.json"
                 })
         {
             CAPTURE(filename);
@@ -11797,42 +11907,42 @@
 
     SECTION("roundtrip")
     {
-        // test cases are from https://github.com/miloyip/nativejson-benchmark/tree/master/data/roundtrip
+        // test cases are from https://github.com/miloyip/nativejson-benchmark/tree/master/test/data/roundtrip
 
         for (auto filename :
                 {
-                    "test/json_roundtrip/roundtrip01.json",
-                    "test/json_roundtrip/roundtrip02.json",
-                    "test/json_roundtrip/roundtrip03.json",
-                    "test/json_roundtrip/roundtrip04.json",
-                    "test/json_roundtrip/roundtrip05.json",
-                    "test/json_roundtrip/roundtrip06.json",
-                    "test/json_roundtrip/roundtrip07.json",
-                    "test/json_roundtrip/roundtrip08.json",
-                    "test/json_roundtrip/roundtrip09.json",
-                    "test/json_roundtrip/roundtrip10.json",
-                    "test/json_roundtrip/roundtrip11.json",
-                    "test/json_roundtrip/roundtrip12.json",
-                    "test/json_roundtrip/roundtrip13.json",
-                    "test/json_roundtrip/roundtrip14.json",
-                    "test/json_roundtrip/roundtrip15.json",
-                    "test/json_roundtrip/roundtrip16.json",
-                    "test/json_roundtrip/roundtrip17.json",
-                    "test/json_roundtrip/roundtrip18.json",
-                    "test/json_roundtrip/roundtrip19.json",
-                    "test/json_roundtrip/roundtrip20.json",
-                    "test/json_roundtrip/roundtrip21.json",
-                    "test/json_roundtrip/roundtrip22.json",
-                    "test/json_roundtrip/roundtrip23.json",
-                    "test/json_roundtrip/roundtrip24.json",
-                    "test/json_roundtrip/roundtrip25.json",
-                    "test/json_roundtrip/roundtrip26.json",
-                    "test/json_roundtrip/roundtrip27.json",
-                    "test/json_roundtrip/roundtrip28.json",
-                    "test/json_roundtrip/roundtrip29.json",
-                    "test/json_roundtrip/roundtrip30.json",
-                    "test/json_roundtrip/roundtrip31.json",
-                    "test/json_roundtrip/roundtrip32.json"
+                    "test/data/json_roundtrip/roundtrip01.json",
+                    "test/data/json_roundtrip/roundtrip02.json",
+                    "test/data/json_roundtrip/roundtrip03.json",
+                    "test/data/json_roundtrip/roundtrip04.json",
+                    "test/data/json_roundtrip/roundtrip05.json",
+                    "test/data/json_roundtrip/roundtrip06.json",
+                    "test/data/json_roundtrip/roundtrip07.json",
+                    "test/data/json_roundtrip/roundtrip08.json",
+                    "test/data/json_roundtrip/roundtrip09.json",
+                    "test/data/json_roundtrip/roundtrip10.json",
+                    "test/data/json_roundtrip/roundtrip11.json",
+                    "test/data/json_roundtrip/roundtrip12.json",
+                    "test/data/json_roundtrip/roundtrip13.json",
+                    "test/data/json_roundtrip/roundtrip14.json",
+                    "test/data/json_roundtrip/roundtrip15.json",
+                    "test/data/json_roundtrip/roundtrip16.json",
+                    "test/data/json_roundtrip/roundtrip17.json",
+                    "test/data/json_roundtrip/roundtrip18.json",
+                    "test/data/json_roundtrip/roundtrip19.json",
+                    "test/data/json_roundtrip/roundtrip20.json",
+                    "test/data/json_roundtrip/roundtrip21.json",
+                    "test/data/json_roundtrip/roundtrip22.json",
+                    "test/data/json_roundtrip/roundtrip23.json",
+                    //"test/data/json_roundtrip/roundtrip24.json", // roundtrip error
+                    //"test/data/json_roundtrip/roundtrip25.json", // roundtrip error
+                    //"test/data/json_roundtrip/roundtrip26.json", // roundtrip error
+                    //"test/data/json_roundtrip/roundtrip27.json", // roundtrip error
+                    //"test/data/json_roundtrip/roundtrip28.json", // roundtrip error
+                    "test/data/json_roundtrip/roundtrip29.json",
+                    //"test/data/json_roundtrip/roundtrip30.json", // roundtrip error
+                    //"test/data/json_roundtrip/roundtrip31.json", // roundtrip error
+                    "test/data/json_roundtrip/roundtrip32.json"
                 })
         {
             CAPTURE(filename);
@@ -11852,7 +11962,7 @@
     {
         // read a file with all unicode characters stored as single-character
         // strings in a JSON array
-        std::ifstream f("test/json_testsuite/sample.json");
+        std::ifstream f("test/data/json_testsuite/sample.json");
         json j;
         CHECK_NOTHROW(j << f);
 
@@ -11867,35 +11977,35 @@
 
     SECTION("1.json")
     {
-        std::ifstream f("test/json.org/1.json");
+        std::ifstream f("test/data/json.org/1.json");
         json j;
         CHECK_NOTHROW(j << f);
     }
 
     SECTION("2.json")
     {
-        std::ifstream f("test/json.org/2.json");
+        std::ifstream f("test/data/json.org/2.json");
         json j;
         CHECK_NOTHROW(j << f);
     }
 
     SECTION("3.json")
     {
-        std::ifstream f("test/json.org/3.json");
+        std::ifstream f("test/data/json.org/3.json");
         json j;
         CHECK_NOTHROW(j << f);
     }
 
     SECTION("4.json")
     {
-        std::ifstream f("test/json.org/4.json");
+        std::ifstream f("test/data/json.org/4.json");
         json j;
         CHECK_NOTHROW(j << f);
     }
 
     SECTION("5.json")
     {
-        std::ifstream f("test/json.org/5.json");
+        std::ifstream f("test/data/json.org/5.json");
         json j;
         CHECK_NOTHROW(j << f);
     }
@@ -12029,18 +12139,48 @@
     {
         // read a file with all unicode characters stored as single-character
         // strings in a JSON array
-        std::ifstream f("test/json_nlohmann_tests/all_unicode.json");
+        std::ifstream f("test/data/json_nlohmann_tests/all_unicode.json");
         json j;
         CHECK_NOTHROW(j << f);
 
         // the array has 1112064 + 1 elemnts (a terminating "null" value)
         CHECK(j.size() == 1112065);
+
+        SECTION("check JSON Pointers")
+        {
+            for (auto s : j)
+            {
+                // skip non-string JSON values
+                if (not s.is_string())
+                {
+                    continue;
+                }
+
+                std::string ptr = s;
+
+                // tilde must be followed by 0 or 1
+                if (ptr == "~")
+                {
+                    ptr += "0";
+                }
+
+                // JSON Pointers must begin with "/"
+                ptr = "/" + ptr;
+
+                CHECK_NOTHROW(json::json_pointer("/" + ptr));
+
+                // check escape/unescape roundtrip
+                auto escaped = json::json_pointer::escape(ptr);
+                json::json_pointer::unescape(escaped);
+                CHECK(escaped == ptr);
+            }
+        }
     }
 
     SECTION("ignore byte-order-mark")
     {
         // read a file with a UTF-8 BOM
-        std::ifstream f("test/json_nlohmann_tests/bom.json");
+        std::ifstream f("test/data/json_nlohmann_tests/bom.json");
         json j;
         CHECK_NOTHROW(j << f);
     }
@@ -12052,6 +12192,1546 @@
     }
 }
 
+TEST_CASE("JSON pointers")
+{
+    SECTION("errors")
+    {
+        CHECK_THROWS_AS(json::json_pointer("foo"), std::domain_error);
+        CHECK_THROWS_WITH(json::json_pointer("foo"), "JSON pointer must be empty or begin with '/'");
+
+        CHECK_THROWS_AS(json::json_pointer("/~~"), std::domain_error);
+        CHECK_THROWS_WITH(json::json_pointer("/~~"), "escape error: '~' must be followed with '0' or '1'");
+
+        CHECK_THROWS_AS(json::json_pointer("/~"), std::domain_error);
+        CHECK_THROWS_WITH(json::json_pointer("/~"), "escape error: '~' must be followed with '0' or '1'");
+
+        json::json_pointer p;
+        CHECK_THROWS_AS(p.top(), std::domain_error);
+        CHECK_THROWS_WITH(p.top(), "JSON pointer has no parent");
+        CHECK_THROWS_AS(p.pop_back(), std::domain_error);
+        CHECK_THROWS_WITH(p.pop_back(), "JSON pointer has no parent");
+    }
+
+    SECTION("examples from RFC 6901")
+    {
+        SECTION("nonconst access")
+        {
+            json j = R"(
+            {
+                "foo": ["bar", "baz"],
+                "": 0,
+                "a/b": 1,
+                "c%d": 2,
+                "e^f": 3,
+                "g|h": 4,
+                "i\\j": 5,
+                "k\"l": 6,
+                " ": 7,
+                "m~n": 8
+            }
+            )"_json;
+
+            // the whole document
+            CHECK(j[json::json_pointer()] == j);
+            CHECK(j[json::json_pointer("")] == j);
+
+            // array access
+            CHECK(j[json::json_pointer("/foo")] == j["foo"]);
+            CHECK(j[json::json_pointer("/foo/0")] == j["foo"][0]);
+            CHECK(j[json::json_pointer("/foo/1")] == j["foo"][1]);
+            CHECK(j["/foo/1"_json_pointer] == j["foo"][1]);
+
+            // checked array access
+            CHECK(j.at(json::json_pointer("/foo/0")) == j["foo"][0]);
+            CHECK(j.at(json::json_pointer("/foo/1")) == j["foo"][1]);
+
+            // empty string access
+            CHECK(j[json::json_pointer("/")] == j[""]);
+
+            // other cases
+            CHECK(j[json::json_pointer("/ ")] == j[" "]);
+            CHECK(j[json::json_pointer("/c%d")] == j["c%d"]);
+            CHECK(j[json::json_pointer("/e^f")] == j["e^f"]);
+            CHECK(j[json::json_pointer("/g|h")] == j["g|h"]);
+            CHECK(j[json::json_pointer("/i\\j")] == j["i\\j"]);
+            CHECK(j[json::json_pointer("/k\"l")] == j["k\"l"]);
+
+            // checked access
+            CHECK(j.at(json::json_pointer("/ ")) == j[" "]);
+            CHECK(j.at(json::json_pointer("/c%d")) == j["c%d"]);
+            CHECK(j.at(json::json_pointer("/e^f")) == j["e^f"]);
+            CHECK(j.at(json::json_pointer("/g|h")) == j["g|h"]);
+            CHECK(j.at(json::json_pointer("/i\\j")) == j["i\\j"]);
+            CHECK(j.at(json::json_pointer("/k\"l")) == j["k\"l"]);
+
+            // escaped access
+            CHECK(j[json::json_pointer("/a~1b")] == j["a/b"]);
+            CHECK(j[json::json_pointer("/m~0n")] == j["m~n"]);
+
+            // unescaped access
+            CHECK_THROWS_AS(j[json::json_pointer("/a/b")], std::out_of_range);
+            CHECK_THROWS_WITH(j[json::json_pointer("/a/b")], "unresolved reference token 'b'");
+            // "/a/b" works for JSON {"a": {"b": 42}}
+            CHECK(json({{"a", {{"b", 42}}}})[json::json_pointer("/a/b")] == json(42));
+
+            // unresolved access
+            json j_primitive = 1;
+            CHECK_THROWS_AS(j_primitive["/foo"_json_pointer], std::out_of_range);
+            CHECK_THROWS_WITH(j_primitive["/foo"_json_pointer], "unresolved reference token 'foo'");
+            CHECK_THROWS_AS(j_primitive.at("/foo"_json_pointer), std::out_of_range);
+            CHECK_THROWS_WITH(j_primitive.at("/foo"_json_pointer), "unresolved reference token 'foo'");
+        }
+
+        SECTION("const access")
+        {
+            const json j = R"(
+            {
+                "foo": ["bar", "baz"],
+                "": 0,
+                "a/b": 1,
+                "c%d": 2,
+                "e^f": 3,
+                "g|h": 4,
+                "i\\j": 5,
+                "k\"l": 6,
+                " ": 7,
+                "m~n": 8
+            }
+            )"_json;
+
+            // the whole document
+            CHECK(j[json::json_pointer()] == j);
+            CHECK(j[json::json_pointer("")] == j);
+
+            // array access
+            CHECK(j[json::json_pointer("/foo")] == j["foo"]);
+            CHECK(j[json::json_pointer("/foo/0")] == j["foo"][0]);
+            CHECK(j[json::json_pointer("/foo/1")] == j["foo"][1]);
+            CHECK(j["/foo/1"_json_pointer] == j["foo"][1]);
+
+            // checked array access
+            CHECK(j.at(json::json_pointer("/foo/0")) == j["foo"][0]);
+            CHECK(j.at(json::json_pointer("/foo/1")) == j["foo"][1]);
+
+            // empty string access
+            CHECK(j[json::json_pointer("/")] == j[""]);
+
+            // other cases
+            CHECK(j[json::json_pointer("/ ")] == j[" "]);
+            CHECK(j[json::json_pointer("/c%d")] == j["c%d"]);
+            CHECK(j[json::json_pointer("/e^f")] == j["e^f"]);
+            CHECK(j[json::json_pointer("/g|h")] == j["g|h"]);
+            CHECK(j[json::json_pointer("/i\\j")] == j["i\\j"]);
+            CHECK(j[json::json_pointer("/k\"l")] == j["k\"l"]);
+
+            // checked access
+            CHECK(j.at(json::json_pointer("/ ")) == j[" "]);
+            CHECK(j.at(json::json_pointer("/c%d")) == j["c%d"]);
+            CHECK(j.at(json::json_pointer("/e^f")) == j["e^f"]);
+            CHECK(j.at(json::json_pointer("/g|h")) == j["g|h"]);
+            CHECK(j.at(json::json_pointer("/i\\j")) == j["i\\j"]);
+            CHECK(j.at(json::json_pointer("/k\"l")) == j["k\"l"]);
+
+            // escaped access
+            CHECK(j[json::json_pointer("/a~1b")] == j["a/b"]);
+            CHECK(j[json::json_pointer("/m~0n")] == j["m~n"]);
+
+            // unescaped access
+            CHECK_THROWS_AS(j.at(json::json_pointer("/a/b")), std::out_of_range);
+            CHECK_THROWS_WITH(j.at(json::json_pointer("/a/b")), "key 'a' not found");
+
+            // unresolved access
+            const json j_primitive = 1;
+            CHECK_THROWS_AS(j_primitive["/foo"_json_pointer], std::out_of_range);
+            CHECK_THROWS_WITH(j_primitive["/foo"_json_pointer], "unresolved reference token 'foo'");
+            CHECK_THROWS_AS(j_primitive.at("/foo"_json_pointer), std::out_of_range);
+            CHECK_THROWS_WITH(j_primitive.at("/foo"_json_pointer), "unresolved reference token 'foo'");
+        }
+
+        SECTION("user-defined string literal")
+        {
+            json j = R"(
+            {
+                "foo": ["bar", "baz"],
+                "": 0,
+                "a/b": 1,
+                "c%d": 2,
+                "e^f": 3,
+                "g|h": 4,
+                "i\\j": 5,
+                "k\"l": 6,
+                " ": 7,
+                "m~n": 8
+            }
+            )"_json;
+
+            // the whole document
+            CHECK(j[""_json_pointer] == j);
+
+            // array access
+            CHECK(j["/foo"_json_pointer] == j["foo"]);
+            CHECK(j["/foo/0"_json_pointer] == j["foo"][0]);
+            CHECK(j["/foo/1"_json_pointer] == j["foo"][1]);
+        }
+    }
+
+    SECTION("array access")
+    {
+        SECTION("nonconst access")
+        {
+            json j = {1, 2, 3};
+            const json j_const = j;
+
+            // check reading access
+            CHECK(j["/0"_json_pointer] == j[0]);
+            CHECK(j["/1"_json_pointer] == j[1]);
+            CHECK(j["/2"_json_pointer] == j[2]);
+
+            // assign to existing index
+            j["/1"_json_pointer] = 13;
+            CHECK(j[1] == json(13));
+
+            // assign to nonexisting index
+            j["/3"_json_pointer] = 33;
+            CHECK(j[3] == json(33));
+
+            // assign to nonexisting index (with gap)
+            j["/5"_json_pointer] = 55;
+            CHECK(j == json({1, 13, 3, 33, nullptr, 55}));
+
+            // error with leading 0
+            CHECK_THROWS_AS(j["/01"_json_pointer], std::domain_error);
+            CHECK_THROWS_WITH(j["/01"_json_pointer], "array index must not begin with '0'");
+            CHECK_THROWS_AS(j_const["/01"_json_pointer], std::domain_error);
+            CHECK_THROWS_WITH(j_const["/01"_json_pointer], "array index must not begin with '0'");
+            CHECK_THROWS_AS(j.at("/01"_json_pointer), std::domain_error);
+            CHECK_THROWS_WITH(j.at("/01"_json_pointer), "array index must not begin with '0'");
+            CHECK_THROWS_AS(j_const.at("/01"_json_pointer), std::domain_error);
+            CHECK_THROWS_WITH(j_const.at("/01"_json_pointer), "array index must not begin with '0'");
+
+            // error with incorrect numbers
+            CHECK_THROWS_AS(j["/one"_json_pointer] = 1, std::invalid_argument);
+
+            // assign to "-"
+            j["/-"_json_pointer] = 99;
+            CHECK(j == json({1, 13, 3, 33, nullptr, 55, 99}));
+
+            // error when using "-" in const object
+            CHECK_THROWS_AS(j_const["/-"_json_pointer], std::out_of_range);
+            CHECK_THROWS_WITH(j_const["/-"_json_pointer], "array index '-' (3) is out of range");
+
+            // error when using "-" with at
+            CHECK_THROWS_AS(j.at("/-"_json_pointer), std::out_of_range);
+            CHECK_THROWS_WITH(j.at("/-"_json_pointer), "array index '-' (7) is out of range");
+            CHECK_THROWS_AS(j_const.at("/-"_json_pointer), std::out_of_range);
+            CHECK_THROWS_WITH(j_const.at("/-"_json_pointer), "array index '-' (3) is out of range");
+        }
+
+        SECTION("const access")
+        {
+            const json j = {1, 2, 3};
+
+            // check reading access
+            CHECK(j["/0"_json_pointer] == j[0]);
+            CHECK(j["/1"_json_pointer] == j[1]);
+            CHECK(j["/2"_json_pointer] == j[2]);
+
+            // assign to nonexisting index
+            CHECK_THROWS_AS(j.at("/3"_json_pointer), std::out_of_range);
+            CHECK_THROWS_WITH(j.at("/3"_json_pointer), "array index 3 is out of range");
+
+            // assign to nonexisting index (with gap)
+            CHECK_THROWS_AS(j.at("/5"_json_pointer), std::out_of_range);
+            CHECK_THROWS_WITH(j.at("/5"_json_pointer), "array index 5 is out of range");
+
+            // assign to "-"
+            CHECK_THROWS_AS(j["/-"_json_pointer], std::out_of_range);
+            CHECK_THROWS_WITH(j["/-"_json_pointer], "array index '-' (3) is out of range");
+            CHECK_THROWS_AS(j.at("/-"_json_pointer), std::out_of_range);
+            CHECK_THROWS_WITH(j.at("/-"_json_pointer), "array index '-' (3) is out of range");
+        }
+
+    }
+
+    SECTION("flatten")
+    {
+        json j =
+        {
+            {"pi", 3.141},
+            {"happy", true},
+            {"name", "Niels"},
+            {"nothing", nullptr},
+            {
+                "answer", {
+                    {"everything", 42}
+                }
+            },
+            {"list", {1, 0, 2}},
+            {
+                "object", {
+                    {"currency", "USD"},
+                    {"value", 42.99},
+                    {"", "empty string"},
+                    {"/", "slash"},
+                    {"~", "tilde"},
+                    {"~1", "tilde1"}
+                }
+            }
+        };
+
+        json j_flatten =
+        {
+            {"/pi", 3.141},
+            {"/happy", true},
+            {"/name", "Niels"},
+            {"/nothing", nullptr},
+            {"/answer/everything", 42},
+            {"/list/0", 1},
+            {"/list/1", 0},
+            {"/list/2", 2},
+            {"/object/currency", "USD"},
+            {"/object/value", 42.99},
+            {"/object/", "empty string"},
+            {"/object/~1", "slash"},
+            {"/object/~0", "tilde"},
+            {"/object/~01", "tilde1"}
+        };
+
+        // check if flattened result is as expected
+        CHECK(j.flatten() == j_flatten);
+
+        // check if unflattened result is as expected
+        CHECK(j_flatten.unflatten() == j);
+
+        // error for nonobjects
+        CHECK_THROWS_AS(json(1).unflatten(), std::domain_error);
+        CHECK_THROWS_WITH(json(1).unflatten(), "only objects can be unflattened");
+
+        // error for nonprimitve values
+        CHECK_THROWS_AS(json({{"/1", {1, 2, 3}}}).unflatten(), std::domain_error);
+        CHECK_THROWS_WITH(json({{"/1", {1, 2, 3}}}).unflatten(), "values in object must be primitive");
+
+        // error for conflicting values
+        json j_error = {{"", 42}, {"/foo", 17}};
+        CHECK_THROWS_AS(j_error.unflatten(), std::domain_error);
+        CHECK_THROWS_WITH(j_error.unflatten(), "invalid value to unflatten");
+
+        // explicit roundtrip check
+        CHECK(j.flatten().unflatten() == j);
+
+        // roundtrip for primitive values
+        json j_null;
+        CHECK(j_null.flatten().unflatten() == j_null);
+        json j_number = 42;
+        CHECK(j_number.flatten().unflatten() == j_number);
+        json j_boolean = false;
+        CHECK(j_boolean.flatten().unflatten() == j_boolean);
+        json j_string = "foo";
+        CHECK(j_string.flatten().unflatten() == j_string);
+
+        // roundtrip for empty structured values (will be unflattened to null)
+        json j_array(json::value_t::array);
+        CHECK(j_array.flatten().unflatten() == json());
+        json j_object(json::value_t::object);
+        CHECK(j_object.flatten().unflatten() == json());
+    }
+
+    SECTION("string representation")
+    {
+        for (auto ptr :
+                {"", "/foo", "/foo/0", "/", "/a~1b", "/c%d", "/e^f", "/g|h", "/i\\j", "/k\"l", "/ ", "/m~0n"
+                })
+        {
+            CHECK(json::json_pointer(ptr).to_string() == ptr);
+        }
+    }
+}
+
+TEST_CASE("JSON patch")
+{
+    SECTION("examples from RFC 6902")
+    {
+        SECTION("4. Operations")
+        {
+            // the ordering of members in JSON objects is not significant:
+            json op1 = R"({ "op": "add", "path": "/a/b/c", "value": "foo" })"_json;
+            json op2 = R"({ "path": "/a/b/c", "op": "add", "value": "foo" })"_json;
+            json op3 = R"({ "value": "foo", "path": "/a/b/c", "op": "add" })"_json;
+
+            // check if the operation objects are equivalent
+            CHECK(op1 == op2);
+            CHECK(op1 == op3);
+        }
+
+        SECTION("4.1 add")
+        {
+            json patch = R"([{ "op": "add", "path": "/a/b/c", "value": [ "foo", "bar" ] }])"_json;
+
+            // However, the object itself or an array containing it does need
+            // to exist, and it remains an error for that not to be the case.
+            // For example, an "add" with a target location of "/a/b" starting
+            // with this document
+            json doc1 = R"({ "a": { "foo": 1 } })"_json;
+
+            // is not an error, because "a" exists, and "b" will be added to
+            // its value.
+            CHECK_NOTHROW(doc1.patch(patch));
+            CHECK(doc1.patch(patch) == R"(
+                {
+                    "a": {
+                        "foo": 1,
+                        "b": {
+                            "c": [ "foo", "bar" ]
+                        }
+                    }
+                }
+            )"_json);
+
+            // It is an error in this document:
+            json doc2 = R"({ "q": { "bar": 2 } })"_json;
+
+            // because "a" does not exist.
+            CHECK_THROWS_AS(doc2.patch(patch), std::out_of_range);
+            CHECK_THROWS_WITH(doc2.patch(patch), "key 'a' not found");
+        }
+
+        SECTION("4.2 remove")
+        {
+            // If removing an element from an array, any elements above the
+            // specified index are shifted one position to the left.
+            json doc = {1, 2, 3, 4};
+            json patch = {{{"op", "remove"}, {"path", "/1"}}};
+            CHECK(doc.patch(patch) == json({1, 3, 4}));
+        }
+
+        SECTION("A.1. Adding an Object Member")
+        {
+            // An example target JSON document:
+            json doc = R"(
+                    { "foo": "bar"}
+                )"_json;
+
+            // A JSON Patch document:
+            json patch = R"(
+                    [
+                        { "op": "add", "path": "/baz", "value": "qux" }
+                    ]
+                )"_json;
+
+            // The resulting JSON document:
+            json expected = R"(
+                    {
+                        "baz": "qux",
+                        "foo": "bar"
+                    }
+                )"_json;
+
+            // check if patched value is as expected
+            CHECK(doc.patch(patch) == expected);
+
+            // check roundtrip
+            CHECK(doc.patch(json::diff(doc, expected)) == expected);
+        }
+
+        SECTION("A.2. Adding an Array Element")
+        {
+            // An example target JSON document:
+            json doc = R"(
+                    { "foo": [ "bar", "baz" ] }
+                )"_json;
+
+            // A JSON Patch document:
+            json patch = R"(
+                    [
+                        { "op": "add", "path": "/foo/1", "value": "qux" }
+                    ]
+                )"_json;
+
+            // The resulting JSON document:
+            json expected = R"(
+                    { "foo": [ "bar", "qux", "baz" ] }
+                )"_json;
+
+            // check if patched value is as expected
+            CHECK(doc.patch(patch) == expected);
+
+            // check roundtrip
+            CHECK(doc.patch(json::diff(doc, expected)) == expected);
+        }
+
+        SECTION("A.3. Removing an Object Member")
+        {
+            // An example target JSON document:
+            json doc = R"(
+                    {
+                        "baz": "qux",
+                        "foo": "bar"
+                    }
+                )"_json;
+
+            // A JSON Patch document:
+            json patch = R"(
+                    [
+                        { "op": "remove", "path": "/baz" }
+                    ]
+                )"_json;
+
+            // The resulting JSON document:
+            json expected = R"(
+                    { "foo": "bar" }
+                )"_json;
+
+            // check if patched value is as expected
+            CHECK(doc.patch(patch) == expected);
+
+            // check roundtrip
+            CHECK(doc.patch(json::diff(doc, expected)) == expected);
+        }
+
+        SECTION("A.4. Removing an Array Element")
+        {
+            // An example target JSON document:
+            json doc = R"(
+                    { "foo": [ "bar", "qux", "baz" ] }
+                )"_json;
+
+            // A JSON Patch document:
+            json patch = R"(
+                    [
+                        { "op": "remove", "path": "/foo/1" }
+                    ]
+                )"_json;
+
+            // The resulting JSON document:
+            json expected = R"(
+                    { "foo": [ "bar", "baz" ] }
+                )"_json;
+
+            // check if patched value is as expected
+            CHECK(doc.patch(patch) == expected);
+
+            // check roundtrip
+            CHECK(doc.patch(json::diff(doc, expected)) == expected);
+        }
+
+        SECTION("A.5. Replacing a Value")
+        {
+            // An example target JSON document:
+            json doc = R"(
+                    {
+                        "baz": "qux",
+                        "foo": "bar"
+                    }
+                )"_json;
+
+            // A JSON Patch document:
+            json patch = R"(
+                    [
+                        { "op": "replace", "path": "/baz", "value": "boo" }
+                    ]
+                )"_json;
+
+            json expected = R"(
+                    {
+                        "baz": "boo",
+                        "foo": "bar"
+                    }
+                )"_json;
+
+            // check if patched value is as expected
+            CHECK(doc.patch(patch) == expected);
+
+            // check roundtrip
+            CHECK(doc.patch(json::diff(doc, expected)) == expected);
+        }
+
+        SECTION("A.6. Moving a Value")
+        {
+            // An example target JSON document:
+            json doc = R"(
+                    {
+                        "foo": {
+                           "bar": "baz",
+                            "waldo": "fred"
+                        },
+                        "qux": {
+                            "corge": "grault"
+                        }
+                    }
+                )"_json;
+
+            // A JSON Patch document:
+            json patch = R"(
+                    [
+                        { "op": "move", "from": "/foo/waldo", "path": "/qux/thud" }
+                    ]
+                )"_json;
+
+            // The resulting JSON document:
+            json expected = R"(
+                    {
+                        "foo": {
+                           "bar": "baz"
+                        },
+                        "qux": {
+                            "corge": "grault",
+                            "thud": "fred"
+                        }
+                    }
+                )"_json;
+
+            // check if patched value is as expected
+            CHECK(doc.patch(patch) == expected);
+
+            // check roundtrip
+            CHECK(doc.patch(json::diff(doc, expected)) == expected);
+        }
+
+        SECTION("A.7. Moving a Value")
+        {
+            // An example target JSON document:
+            json doc = R"(
+                    { "foo": [ "all", "grass", "cows", "eat" ] }
+                )"_json;
+
+            // A JSON Patch document:
+            json patch = R"(
+                    [
+                        { "op": "move", "from": "/foo/1", "path": "/foo/3" }
+                    ]
+                )"_json;
+
+            // The resulting JSON document:
+            json expected = R"(
+                    { "foo": [ "all", "cows", "eat", "grass" ] }
+                )"_json;
+
+            // check if patched value is as expected
+            CHECK(doc.patch(patch) == expected);
+
+            // check roundtrip
+            CHECK(doc.patch(json::diff(doc, expected)) == expected);
+        }
+
+        SECTION("A.8. Testing a Value: Success")
+        {
+            // An example target JSON document:
+            json doc = R"(
+                    {
+                         "baz": "qux",
+                         "foo": [ "a", 2, "c" ]
+                    }
+                )"_json;
+
+            // A JSON Patch document that will result in successful evaluation:
+            json patch = R"(
+                    [
+                        { "op": "test", "path": "/baz", "value": "qux" },
+                        { "op": "test", "path": "/foo/1", "value": 2 }
+                    ]
+                )"_json;
+
+            // check if evaluation does not throw
+            CHECK_NOTHROW(doc.patch(patch));
+            // check if patched document is unchanged
+            CHECK(doc.patch(patch) == doc);
+        }
+
+        SECTION("A.9. Testing a Value: Error")
+        {
+            // An example target JSON document:
+            json doc = R"(
+                    { "baz": "qux" }
+                )"_json;
+
+            // A JSON Patch document that will result in an error condition:
+            json patch = R"(
+                    [
+                        { "op": "test", "path": "/baz", "value": "bar" }
+                    ]
+                )"_json;
+
+            // check that evaluation throws
+            CHECK_THROWS_AS(doc.patch(patch), std::domain_error);
+            CHECK_THROWS_WITH(doc.patch(patch), "unsuccessful: " + patch[0].dump());
+        }
+
+        SECTION("A.10. Adding a Nested Member Object")
+        {
+            // An example target JSON document:
+            json doc = R"(
+                    { "foo": "bar" }
+                )"_json;
+
+            // A JSON Patch document:
+            json patch = R"(
+                    [
+                        { "op": "add", "path": "/child", "value": { "grandchild": { } } }
+                    ]
+                )"_json;
+
+            // The resulting JSON document:
+            json expected = R"(
+                {
+                    "foo": "bar",
+                    "child": {
+                        "grandchild": {
+                        }
+                    }
+                }
+                )"_json;
+
+            // check if patched value is as expected
+            CHECK(doc.patch(patch) == expected);
+
+            // check roundtrip
+            CHECK(doc.patch(json::diff(doc, expected)) == expected);
+        }
+
+        SECTION("A.11. Ignoring Unrecognized Elements")
+        {
+            // An example target JSON document:
+            json doc = R"(
+                    { "foo": "bar" }
+                )"_json;
+
+            // A JSON Patch document:
+            json patch = R"(
+                    [
+                        { "op": "add", "path": "/baz", "value": "qux", "xyz": 123 }
+                    ]
+                )"_json;
+
+            json expected = R"(
+                    {
+                        "foo": "bar",
+                        "baz": "qux"
+                    } 
+                )"_json;
+
+            // check if patched value is as expected
+            CHECK(doc.patch(patch) == expected);
+
+            // check roundtrip
+            CHECK(doc.patch(json::diff(doc, expected)) == expected);
+        }
+
+        SECTION("A.12. Adding to a Nonexistent Target")
+        {
+            // An example target JSON document:
+            json doc = R"(
+                    { "foo": "bar" }
+                )"_json;
+
+            // A JSON Patch document:
+            json patch = R"(
+                    [
+                        { "op": "add", "path": "/baz/bat", "value": "qux" }
+                    ]
+                )"_json;
+
+            // This JSON Patch document, applied to the target JSON document
+            // above, would result in an error (therefore, it would not be
+            // applied), because the "add" operation's target location that
+            // references neither the root of the document, nor a member of
+            // an existing object, nor a member of an existing array.
+
+            CHECK_THROWS_AS(doc.patch(patch), std::out_of_range);
+            CHECK_THROWS_WITH(doc.patch(patch), "key 'baz' not found");
+        }
+
+        // A.13. Invalid JSON Patch Document
+        // not applicable
+
+        SECTION("A.14. Escape Ordering")
+        {
+            // An example target JSON document:
+            json doc = R"(
+                    {
+                        "/": 9,
+                        "~1": 10
+                    }
+                )"_json;
+
+            // A JSON Patch document:
+            json patch = R"(
+                    [
+                        {"op": "test", "path": "/~01", "value": 10}
+                    ]
+                )"_json;
+
+            json expected = R"(
+                    {
+                        "/": 9,
+                        "~1": 10
+                    } 
+                )"_json;
+
+            // check if patched value is as expected
+            CHECK(doc.patch(patch) == expected);
+
+            // check roundtrip
+            CHECK(doc.patch(json::diff(doc, expected)) == expected);
+        }
+
+        SECTION("A.15. Comparing Strings and Numbers")
+        {
+            // An example target JSON document:
+            json doc = R"(
+                    {
+                        "/": 9,
+                        "~1": 10
+                    } 
+                )"_json;
+
+            // A JSON Patch document that will result in an error condition:
+            json patch = R"(
+                    [
+                        {"op": "test", "path": "/~01", "value": "10"}
+                    ]
+                )"_json;
+
+            // check that evaluation throws
+            CHECK_THROWS_AS(doc.patch(patch), std::domain_error);
+            CHECK_THROWS_WITH(doc.patch(patch), "unsuccessful: " + patch[0].dump());
+        }
+
+        SECTION("A.16. Adding an Array Value")
+        {
+            // An example target JSON document:
+            json doc = R"(
+                    { "foo": ["bar"] }
+                )"_json;
+
+            // A JSON Patch document:
+            json patch = R"(
+                    [
+                        { "op": "add", "path": "/foo/-", "value": ["abc", "def"] }
+                    ]
+                )"_json;
+
+            // The resulting JSON document:
+            json expected = R"(
+                    { "foo": ["bar", ["abc", "def"]] }
+                )"_json;
+
+            // check if patched value is as expected
+            CHECK(doc.patch(patch) == expected);
+
+            // check roundtrip
+            CHECK(doc.patch(json::diff(doc, expected)) == expected);
+        }
+    }
+
+    SECTION("own examples")
+    {
+        SECTION("add")
+        {
+            SECTION("add to the root element")
+            {
+                // If the path is the root of the target document - the
+                // specified value becomes the entire content of the target
+                // document.
+
+                // An example target JSON document:
+                json doc = 17;
+
+                // A JSON Patch document:
+                json patch = R"(
+                        [
+                            { "op": "add", "path": "", "value": [1,2,3] }
+                        ]
+                    )"_json;
+
+                // The resulting JSON document:
+                json expected = {1, 2, 3};
+
+                // check if patched value is as expected
+                CHECK(doc.patch(patch) == expected);
+
+                // check roundtrip
+                CHECK(doc.patch(json::diff(doc, expected)) == expected);
+            }
+
+            SECTION("add to end of the array")
+            {
+                // The specified index MUST NOT be greater than the number of
+                // elements in the array. The example below uses and index of
+                // exactly the number of elements in the array which is legal.
+
+                // An example target JSON document:
+                json doc = {0, 1, 2};
+
+                // A JSON Patch document:
+                json patch = R"(
+                    [
+                        { "op": "add", "path": "/3", "value": 3 }
+                    ]
+                )"_json;
+
+                // The resulting JSON document:
+                json expected = {0, 1, 2, 3};
+
+                // check if patched value is as expected
+                CHECK(doc.patch(patch) == expected);
+
+                // check roundtrip
+                CHECK(doc.patch(json::diff(doc, expected)) == expected);
+            }
+        }
+
+        SECTION("copy")
+        {
+            // An example target JSON document:
+            json doc = R"(
+                {
+                    "foo": {
+                        "bar": "baz",
+                        "waldo": "fred"
+                    },
+                    "qux": {
+                       "corge": "grault"
+                    }
+                }
+            )"_json;
+
+            // A JSON Patch document:
+            json patch = R"(
+                [
+                    { "op": "copy", "from": "/foo/waldo", "path": "/qux/thud" }
+                ]
+            )"_json;
+
+            // The resulting JSON document:
+            json expected = R"(
+                {
+                    "foo": {
+                        "bar": "baz",
+                        "waldo": "fred"
+                    },
+                    "qux": {
+                       "corge": "grault",
+                       "thud": "fred"
+                    }
+                }
+            )"_json;
+
+            // check if patched value is as expected
+            CHECK(doc.patch(patch) == expected);
+
+            // check roundtrip
+            CHECK(doc.patch(json::diff(doc, expected)) == expected);
+        }
+
+        SECTION("replace")
+        {
+            json j = "string";
+            json patch = {{{"op", "replace"}, {"path", ""}, {"value", 1}}};
+            CHECK(j.patch(patch) == json(1));
+        }
+
+        SECTION("documentation GIF")
+        {
+            {
+                // a JSON patch
+                json p1 = R"(
+                     [{"op": "add", "path": "/GB", "value": "London"}]
+                    )"_json;
+
+                // a JSON value
+                json source = R"(
+                      {"D": "Berlin", "F": "Paris"}
+                    )"_json;
+
+                // apply the patch
+                json target = source.patch(p1);
+                // target = { "D": "Berlin", "F": "Paris", "GB": "London" }
+                CHECK(target == R"({ "D": "Berlin", "F": "Paris", "GB": "London" })"_json);
+
+                // create a diff from two JSONs
+                json p2 = json::diff(target, source);
+                // p2 = [{"op": "delete", "path": "/GB"}]
+                CHECK(p2 == R"([{"op":"remove","path":"/GB"}])"_json);
+            }
+            {
+                // a JSON value
+                json j = {"good", "bad", "ugly"};
+
+                // a JSON pointer
+                auto ptr = json::json_pointer("/2");
+
+                // use to access elements
+                j[ptr] = {{"it", "cattivo"}};
+                CHECK(j == R"(["good","bad",{"it":"cattivo"}])"_json);
+
+                // use user-defined string literal
+                j["/2/en"_json_pointer] = "ugly";
+                CHECK(j == R"(["good","bad",{"en":"ugly","it":"cattivo"}])"_json);
+
+                json flat = j.flatten();
+                CHECK(flat == R"({"/0":"good","/1":"bad","/2/en":"ugly","/2/it":"cattivo"})"_json);
+            }
+        }
+    }
+
+    SECTION("errors")
+    {
+        SECTION("unknown operation")
+        {
+            SECTION("not an array")
+            {
+                json j;
+                json patch = {{"op", "add"}, {"path", ""}, {"value", 1}};
+                CHECK_THROWS_AS(j.patch(patch), std::invalid_argument);
+                CHECK_THROWS_WITH(j.patch(patch), "JSON patch must be an array of objects");
+            }
+
+            SECTION("not an array of objects")
+            {
+                json j;
+                json patch = {"op", "add", "path", "", "value", 1};
+                CHECK_THROWS_AS(j.patch(patch), std::invalid_argument);
+                CHECK_THROWS_WITH(j.patch(patch), "JSON patch must be an array of objects");
+            }
+
+            SECTION("missing 'op'")
+            {
+                json j;
+                json patch = {{{"foo", "bar"}}};
+                CHECK_THROWS_AS(j.patch(patch), std::invalid_argument);
+                CHECK_THROWS_WITH(j.patch(patch), "operation must have member 'op'");
+            }
+
+            SECTION("non-string 'op'")
+            {
+                json j;
+                json patch = {{{"op", 1}}};
+                CHECK_THROWS_AS(j.patch(patch), std::invalid_argument);
+                CHECK_THROWS_WITH(j.patch(patch), "operation must have string member 'op'");
+            }
+
+            SECTION("invalid operation")
+            {
+                json j;
+                json patch = {{{"op", "foo"}, {"path", ""}}};
+                CHECK_THROWS_AS(j.patch(patch), std::invalid_argument);
+                CHECK_THROWS_WITH(j.patch(patch), "operation value 'foo' is invalid");
+            }
+        }
+
+        SECTION("add")
+        {
+            SECTION("missing 'path'")
+            {
+                json j;
+                json patch = {{{"op", "add"}}};
+                CHECK_THROWS_AS(j.patch(patch), std::invalid_argument);
+                CHECK_THROWS_WITH(j.patch(patch), "operation 'add' must have member 'path'");
+            }
+
+            SECTION("non-string 'path'")
+            {
+                json j;
+                json patch = {{{"op", "add"}, {"path", 1}}};
+                CHECK_THROWS_AS(j.patch(patch), std::invalid_argument);
+                CHECK_THROWS_WITH(j.patch(patch), "operation 'add' must have string member 'path'");
+            }
+
+            SECTION("missing 'value'")
+            {
+                json j;
+                json patch = {{{"op", "add"}, {"path", ""}}};
+                CHECK_THROWS_AS(j.patch(patch), std::invalid_argument);
+                CHECK_THROWS_WITH(j.patch(patch), "operation 'add' must have member 'value'");
+            }
+
+            SECTION("invalid array index")
+            {
+                json j = {1, 2};
+                json patch = {{{"op", "add"}, {"path", "/4"}, {"value", 4}}};
+                CHECK_THROWS_AS(j.patch(patch), std::out_of_range);
+                CHECK_THROWS_WITH(j.patch(patch), "array index 4 is out of range");
+            }
+        }
+
+        SECTION("remove")
+        {
+            SECTION("missing 'path'")
+            {
+                json j;
+                json patch = {{{"op", "remove"}}};
+                CHECK_THROWS_AS(j.patch(patch), std::invalid_argument);
+                CHECK_THROWS_WITH(j.patch(patch), "operation 'remove' must have member 'path'");
+            }
+
+            SECTION("non-string 'path'")
+            {
+                json j;
+                json patch = {{{"op", "remove"}, {"path", 1}}};
+                CHECK_THROWS_AS(j.patch(patch), std::invalid_argument);
+                CHECK_THROWS_WITH(j.patch(patch), "operation 'remove' must have string member 'path'");
+            }
+
+            SECTION("nonexisting target location (array)")
+            {
+                json j = {1, 2, 3};
+                json patch = {{{"op", "remove"}, {"path", "/17"}}};
+                CHECK_THROWS_AS(j.patch(patch), std::out_of_range);
+                CHECK_THROWS_WITH(j.patch(patch), "array index 17 is out of range");
+            }
+
+            SECTION("nonexisting target location (object)")
+            {
+                json j = {{"foo", 1}, {"bar", 2}};
+                json patch = {{{"op", "remove"}, {"path", "/baz"}}};
+                CHECK_THROWS_AS(j.patch(patch), std::out_of_range);
+                CHECK_THROWS_WITH(j.patch(patch), "key 'baz' not found");
+            }
+
+            SECTION("root element as target location")
+            {
+                json j = "string";
+                json patch = {{{"op", "remove"}, {"path", ""}}};
+                CHECK_THROWS_AS(j.patch(patch), std::domain_error);
+                CHECK_THROWS_WITH(j.patch(patch), "JSON pointer has no parent");
+            }
+        }
+
+        SECTION("replace")
+        {
+            SECTION("missing 'path'")
+            {
+                json j;
+                json patch = {{{"op", "replace"}}};
+                CHECK_THROWS_AS(j.patch(patch), std::invalid_argument);
+                CHECK_THROWS_WITH(j.patch(patch), "operation 'replace' must have member 'path'");
+            }
+
+            SECTION("non-string 'path'")
+            {
+                json j;
+                json patch = {{{"op", "replace"}, {"path", 1}}};
+                CHECK_THROWS_AS(j.patch(patch), std::invalid_argument);
+                CHECK_THROWS_WITH(j.patch(patch), "operation 'replace' must have string member 'path'");
+            }
+
+            SECTION("missing 'value'")
+            {
+                json j;
+                json patch = {{{"op", "replace"}, {"path", ""}}};
+                CHECK_THROWS_AS(j.patch(patch), std::invalid_argument);
+                CHECK_THROWS_WITH(j.patch(patch), "operation 'replace' must have member 'value'");
+            }
+
+            SECTION("nonexisting target location (array)")
+            {
+                json j = {1, 2, 3};
+                json patch = {{{"op", "replace"}, {"path", "/17"}, {"value", 19}}};
+                CHECK_THROWS_AS(j.patch(patch), std::out_of_range);
+                CHECK_THROWS_WITH(j.patch(patch), "array index 17 is out of range");
+            }
+
+            SECTION("nonexisting target location (object)")
+            {
+                json j = {{"foo", 1}, {"bar", 2}};
+                json patch = {{{"op", "replace"}, {"path", "/baz"}, {"value", 3}}};
+                CHECK_THROWS_AS(j.patch(patch), std::out_of_range);
+                CHECK_THROWS_WITH(j.patch(patch), "key 'baz' not found");
+            }
+        }
+
+        SECTION("move")
+        {
+            SECTION("missing 'path'")
+            {
+                json j;
+                json patch = {{{"op", "move"}}};
+                CHECK_THROWS_AS(j.patch(patch), std::invalid_argument);
+                CHECK_THROWS_WITH(j.patch(patch), "operation 'move' must have member 'path'");
+            }
+
+            SECTION("non-string 'path'")
+            {
+                json j;
+                json patch = {{{"op", "move"}, {"path", 1}}};
+                CHECK_THROWS_AS(j.patch(patch), std::invalid_argument);
+                CHECK_THROWS_WITH(j.patch(patch), "operation 'move' must have string member 'path'");
+            }
+
+            SECTION("missing 'from'")
+            {
+                json j;
+                json patch = {{{"op", "move"}, {"path", ""}}};
+                CHECK_THROWS_AS(j.patch(patch), std::invalid_argument);
+                CHECK_THROWS_WITH(j.patch(patch), "operation 'move' must have member 'from'");
+            }
+
+            SECTION("non-string 'from'")
+            {
+                json j;
+                json patch = {{{"op", "move"}, {"path", ""}, {"from", 1}}};
+                CHECK_THROWS_AS(j.patch(patch), std::invalid_argument);
+                CHECK_THROWS_WITH(j.patch(patch), "operation 'move' must have string member 'from'");
+            }
+
+            SECTION("nonexisting from location (array)")
+            {
+                json j = {1, 2, 3};
+                json patch = {{{"op", "move"}, {"path", "/0"}, {"from", "/5"}}};
+                CHECK_THROWS_AS(j.patch(patch), std::out_of_range);
+                CHECK_THROWS_WITH(j.patch(patch), "array index 5 is out of range");
+            }
+
+            SECTION("nonexisting from location (object)")
+            {
+                json j = {{"foo", 1}, {"bar", 2}};
+                json patch = {{{"op", "move"}, {"path", "/baz"}, {"from", "/baz"}}};
+                CHECK_THROWS_AS(j.patch(patch), std::out_of_range);
+                CHECK_THROWS_WITH(j.patch(patch), "key 'baz' not found");
+            }
+        }
+
+        SECTION("copy")
+        {
+            SECTION("missing 'path'")
+            {
+                json j;
+                json patch = {{{"op", "copy"}}};
+                CHECK_THROWS_AS(j.patch(patch), std::invalid_argument);
+                CHECK_THROWS_WITH(j.patch(patch), "operation 'copy' must have member 'path'");
+            }
+
+            SECTION("non-string 'path'")
+            {
+                json j;
+                json patch = {{{"op", "copy"}, {"path", 1}}};
+                CHECK_THROWS_AS(j.patch(patch), std::invalid_argument);
+                CHECK_THROWS_WITH(j.patch(patch), "operation 'copy' must have string member 'path'");
+            }
+
+            SECTION("missing 'from'")
+            {
+                json j;
+                json patch = {{{"op", "copy"}, {"path", ""}}};
+                CHECK_THROWS_AS(j.patch(patch), std::invalid_argument);
+                CHECK_THROWS_WITH(j.patch(patch), "operation 'copy' must have member 'from'");
+            }
+
+            SECTION("non-string 'from'")
+            {
+                json j;
+                json patch = {{{"op", "copy"}, {"path", ""}, {"from", 1}}};
+                CHECK_THROWS_AS(j.patch(patch), std::invalid_argument);
+                CHECK_THROWS_WITH(j.patch(patch), "operation 'copy' must have string member 'from'");
+            }
+
+            SECTION("nonexisting from location (array)")
+            {
+                json j = {1, 2, 3};
+                json patch = {{{"op", "copy"}, {"path", "/0"}, {"from", "/5"}}};
+                CHECK_THROWS_AS(j.patch(patch), std::out_of_range);
+                CHECK_THROWS_WITH(j.patch(patch), "array index 5 is out of range");
+            }
+
+            SECTION("nonexisting from location (object)")
+            {
+                json j = {{"foo", 1}, {"bar", 2}};
+                json patch = {{{"op", "copy"}, {"path", "/fob"}, {"from", "/baz"}}};
+                CHECK_THROWS_AS(j.patch(patch), std::out_of_range);
+                CHECK_THROWS_WITH(j.patch(patch), "key 'baz' not found");
+            }
+        }
+
+        SECTION("test")
+        {
+            SECTION("missing 'path'")
+            {
+                json j;
+                json patch = {{{"op", "test"}}};
+                CHECK_THROWS_AS(j.patch(patch), std::invalid_argument);
+                CHECK_THROWS_WITH(j.patch(patch), "operation 'test' must have member 'path'");
+            }
+
+            SECTION("non-string 'path'")
+            {
+                json j;
+                json patch = {{{"op", "test"}, {"path", 1}}};
+                CHECK_THROWS_AS(j.patch(patch), std::invalid_argument);
+                CHECK_THROWS_WITH(j.patch(patch), "operation 'test' must have string member 'path'");
+            }
+
+            SECTION("missing 'value'")
+            {
+                json j;
+                json patch = {{{"op", "test"}, {"path", ""}}};
+                CHECK_THROWS_AS(j.patch(patch), std::invalid_argument);
+                CHECK_THROWS_WITH(j.patch(patch), "operation 'test' must have member 'value'");
+            }
+        }
+    }
+
+    SECTION("Examples from jsonpatch.com")
+    {
+        SECTION("Simple Example")
+        {
+            // The original document
+            json doc = R"(
+                {
+                  "baz": "qux",
+                  "foo": "bar"
+                }
+            )"_json;
+
+            // The patch
+            json patch = R"(
+                [
+                  { "op": "replace", "path": "/baz", "value": "boo" },
+                  { "op": "add", "path": "/hello", "value": ["world"] },
+                  { "op": "remove", "path": "/foo"}
+                ]
+            )"_json;
+
+            // The result
+            json result = R"(
+                {
+                   "baz": "boo",
+                   "hello": ["world"]
+                }
+            )"_json;
+
+            // check if patched value is as expected
+            CHECK(doc.patch(patch) == result);
+
+            // check roundtrip
+            CHECK(doc.patch(json::diff(doc, result)) == result);
+        }
+
+        SECTION("Operations")
+        {
+            // The original document
+            json doc = R"(
+                {
+                  "biscuits": [
+                    {"name":"Digestive"},
+                    {"name": "Choco Liebniz"}
+                  ]
+                }
+            )"_json;
+
+            SECTION("add")
+            {
+                // The patch
+                json patch = R"(
+                    [
+                        {"op": "add", "path": "/biscuits/1", "value": {"name": "Ginger Nut"}}
+                    ]
+                )"_json;
+
+                // The result
+                json result = R"(
+                    {
+                      "biscuits": [
+                        {"name": "Digestive"},
+                        {"name": "Ginger Nut"},
+                        {"name": "Choco Liebniz"}
+                      ]
+                    }
+                )"_json;
+
+                // check if patched value is as expected
+                CHECK(doc.patch(patch) == result);
+
+                // check roundtrip
+                CHECK(doc.patch(json::diff(doc, result)) == result);
+            }
+
+            SECTION("remove")
+            {
+                // The patch
+                json patch = R"(
+                    [
+                        {"op": "remove", "path": "/biscuits"}
+                    ]
+                )"_json;
+
+                // The result
+                json result = R"(
+                    {}
+                )"_json;
+
+                // check if patched value is as expected
+                CHECK(doc.patch(patch) == result);
+
+                // check roundtrip
+                CHECK(doc.patch(json::diff(doc, result)) == result);
+            }
+
+            SECTION("replace")
+            {
+                // The patch
+                json patch = R"(
+                    [
+                        {"op": "replace", "path": "/biscuits/0/name", "value": "Chocolate Digestive"}
+                    ]
+                )"_json;
+
+                // The result
+                json result = R"(
+                    {
+                      "biscuits": [
+                        {"name": "Chocolate Digestive"},
+                        {"name": "Choco Liebniz"}
+                      ]
+                    }
+                )"_json;
+
+                // check if patched value is as expected
+                CHECK(doc.patch(patch) == result);
+
+                // check roundtrip
+                CHECK(doc.patch(json::diff(doc, result)) == result);
+            }
+
+            SECTION("copy")
+            {
+                // The patch
+                json patch = R"(
+                    [
+                        {"op": "copy", "from": "/biscuits/0", "path": "/best_biscuit"}
+                    ]
+                )"_json;
+
+                // The result
+                json result = R"(
+                    {
+                      "biscuits": [
+                        {"name": "Digestive"},
+                        {"name": "Choco Liebniz"}
+                      ],
+                      "best_biscuit": {
+                        "name": "Digestive"
+                      }
+                    }
+                )"_json;
+
+                // check if patched value is as expected
+                CHECK(doc.patch(patch) == result);
+
+                // check roundtrip
+                CHECK(doc.patch(json::diff(doc, result)) == result);
+            }
+
+            SECTION("move")
+            {
+                // The patch
+                json patch = R"(
+                    [
+                        {"op": "move", "from": "/biscuits", "path": "/cookies"}
+                    ]
+                )"_json;
+
+                // The result
+                json result = R"(
+                    {
+                      "cookies": [
+                        {"name": "Digestive"},
+                        {"name": "Choco Liebniz"}
+                      ]
+                    }
+                )"_json;
+
+                // check if patched value is as expected
+                CHECK(doc.patch(patch) == result);
+
+                // check roundtrip
+                CHECK(doc.patch(json::diff(doc, result)) == result);
+            }
+
+            SECTION("test")
+            {
+                // The patch
+                json patch = R"(
+                    [
+                        {"op": "test", "path": "/best_biscuit/name", "value": "Choco Liebniz"}
+                    ]
+                )"_json;
+
+                // the test will fail
+                CHECK_THROWS_AS(doc.patch(patch), std::domain_error);
+                CHECK_THROWS_WITH(doc.patch(patch), "unsuccessful: " + patch[0].dump());
+            }
+        }
+    }
+
+    SECTION("Examples from bruth.github.io/jsonpatch-js")
+    {
+        SECTION("add")
+        {
+            CHECK(R"( {} )"_json.patch(
+                      R"( [{"op": "add", "path": "/foo", "value": "bar"}] )"_json
+                  ) == R"( {"foo": "bar"} )"_json);
+
+            CHECK(R"( {"foo": [1, 3]} )"_json.patch(
+                      R"( [{"op": "add", "path": "/foo", "value": "bar"}] )"_json
+                  ) == R"( {"foo": "bar"} )"_json);
+
+            CHECK(R"( {"foo": [{}]} )"_json.patch(
+                      R"( [{"op": "add", "path": "/foo/0/bar", "value": "baz"}] )"_json
+                  ) == R"( {"foo": [{"bar": "baz"}]} )"_json);
+        }
+
+        SECTION("remove")
+        {
+            CHECK(R"( {"foo": "bar"} )"_json.patch(
+                      R"( [{"op": "remove", "path": "/foo"}] )"_json
+                  ) == R"( {} )"_json);
+
+            CHECK(R"( {"foo": [1, 2, 3]} )"_json.patch(
+                      R"( [{"op": "remove", "path": "/foo/1"}] )"_json
+                  ) == R"( {"foo": [1, 3]} )"_json);
+
+            CHECK(R"( {"foo": [{"bar": "baz"}]} )"_json.patch(
+                      R"( [{"op": "remove", "path": "/foo/0/bar"}] )"_json
+                  ) == R"( {"foo": [{}]} )"_json);
+        }
+
+        SECTION("replace")
+        {
+            CHECK(R"( {"foo": "bar"} )"_json.patch(
+                      R"( [{"op": "replace", "path": "/foo", "value": 1}] )"_json
+                  ) == R"( {"foo": 1} )"_json);
+
+            CHECK(R"( {"foo": [1, 2, 3]} )"_json.patch(
+                      R"( [{"op": "replace", "path": "/foo/1", "value": 4}] )"_json
+                  ) == R"( {"foo": [1, 4, 3]} )"_json);
+
+            CHECK(R"( {"foo": [{"bar": "baz"}]} )"_json.patch(
+                      R"( [{"op": "replace", "path": "/foo/0/bar", "value": 1}] )"_json
+                  ) == R"( {"foo": [{"bar": 1}]} )"_json);
+        }
+
+        SECTION("move")
+        {
+            CHECK(R"( {"foo": [1, 2, 3]} )"_json.patch(
+                      R"( [{"op": "move", "from": "/foo", "path": "/bar"}] )"_json
+                  ) == R"( {"bar": [1, 2, 3]} )"_json);
+        }
+
+        SECTION("copy")
+        {
+            CHECK(R"( {"foo": [1, 2, 3]} )"_json.patch(
+                      R"( [{"op": "copy", "from": "/foo/1", "path": "/bar"}] )"_json
+                  ) == R"( {"foo": [1, 2, 3], "bar": 2} )"_json);
+        }
+
+        SECTION("copy")
+        {
+            CHECK_NOTHROW(R"( {"foo": "bar"} )"_json.patch(
+                              R"( [{"op": "test", "path": "/foo", "value": "bar"}] )"_json));
+        }
+    }
+}
+
 TEST_CASE("regression tests")
 {
     SECTION("issue #60 - Double quotation mark is not parsed correctly")
@@ -12404,8 +14084,69 @@
         //CHECK(j2b.dump() == "23.42");
 
         CHECK(j3a.dump() == "10000");
-        CHECK(j3b.dump() == "1E04");
-        CHECK(j3c.dump() == "1e04");
+        CHECK(j3b.dump() == "10000");
+        CHECK(j3c.dump() == "10000");
+        //CHECK(j3b.dump() == "1E04"); // roundtrip error
+        //CHECK(j3c.dump() == "1e04"); // roundtrip error
+    }
+
+    SECTION("issue #233 - Can't use basic_json::iterator as a base iterator for std::move_iterator")
+    {
+        json source = {"a", "b", "c"};
+        json expected = {"a", "b"};
+        json dest;
+
+        std::copy_n(std::make_move_iterator(source.begin()), 2, std::back_inserter(dest));
+
+        CHECK(dest == expected);
+    }
+
+    SECTION("issue #235 - ambiguous overload for 'push_back' and 'operator+='")
+    {
+        json data = {{"key", "value"}};
+        data.push_back({"key2", "value2"});
+        data += {"key3", "value3"};
+
+        CHECK(data == json({{"key", "value"}, {"key2", "value2"}, {"key3", "value3"}}));
+    }
+
+    SECTION("issue #269 - diff generates incorrect patch when removing multiple array elements")
+    {
+        json doc = R"( { "arr1": [1, 2, 3, 4] } )"_json;
+        json expected = R"( { "arr1": [1, 2] } )"_json;
+
+        // check roundtrip
+        CHECK(doc.patch(json::diff(doc, expected)) == expected);
     }
 }
 
+// special test case to check if memory is leaked if constructor throws
+
+template<class T>
+struct my_allocator : std::allocator<T>
+{
+    template<class... Args>
+    void construct(T*, Args&& ...)
+    {
+        throw std::bad_alloc();
+    }
+};
+
+TEST_CASE("bad_alloc")
+{
+    SECTION("bad_alloc")
+    {
+        // create JSON type using the throwing allocator
+        using my_json = nlohmann::basic_json<std::map,
+              std::vector,
+              std::string,
+              bool,
+              std::int64_t,
+              std::uint64_t,
+              double,
+              my_allocator>;
+
+        // creating an object should throw
+        CHECK_THROWS_AS(my_json j(my_json::value_t::object), std::bad_alloc);
+    }
+}