Repository: bacpop/pp-sketchlib (language: python)

Issue:
Change interface to python command line
Use docopt for the python CLI
Allow single kmer length comparisons

TODO:
- [x] Check tests run
- [x] Remove printing of options dict
- [x] Remove hard coded sys.path
- [x] Update README
- [x] Check single k-mer length
- ~[ ] Update sketching page of poppunk docs~ now https://github.com/johnlees/PopPUNK/issues/191

Closes #58
Allow non-conda builds and pip install
This PR adds a non-conda build, proven to work with a docker image which we build on buildkite.

To do this:

* HDF5 was put under cmake control
* Had a fight with pybind11's detection
* The HighFive library, removed as a submodule in 075021974f87d1cb8916f04f1479979d70c0006c is now included as a manually updated lib (not a submodule); see scripts/ for the details
* added a MANIFEST.in which seems to be this month's way of controlling what gets included

Note that I can't work out how to get pybind11 to install _before_ pip tries to install the package. It turns out that if that doesn't happen pip installs a hopelessly broken package with almost no warning! I tried both `install_requires` and `setup_requires` but neither seem to behave. If there's any documentation apart from random SO threads we might be able to find out the correct way of making this work.

**UPDATE**: this pybind11 dependency resolution thing is extremely annoying in practice. There must be some way of declaring a dependency in setup.py that setup.py actually respects? If you know it John, please share. This is not promising (https://github.com/pybind/python_example/issues/20) - my quick read "you can't do this thing, build a wheel instead, enjoy shipping broken source packages"

@johnlees; you will need to update the Azure pipelines to point at the moved repo - they have not run here.


Discussion / hints:

Fix patch:
diff --git a/.dockerignore b/.dockerignore
new file mode 100644
index 00000000..a4141bfd
--- /dev/null
+++ b/.dockerignore
@@ -0,0 +1,4 @@
+build
+dist
+*.egg-info
+.git
diff --git a/.gitignore b/.gitignore
index d863eb24..2312ee10 100644
--- a/.gitignore
+++ b/.gitignore
@@ -61,6 +61,7 @@ __pycache__/
 env/
 build/
 develop-eggs/
+dist/
 dist/*.egg
 downloads/
 eggs/
diff --git a/CMakeLists.txt b/CMakeLists.txt
index 99cdc47b..c231c7ab 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -1,7 +1,10 @@
-cmake_minimum_required(VERSION 3.19)
+cmake_minimum_required(VERSION 3.16)
 project(pp_sketchlib)
 set(CMAKE_CXX_STANDARD 14)
-cmake_policy(SET CMP0104 OLD) # Can't get CUDA_ARCHITECTURES to work with NEW
+
+if (${CMAKE_VERSION} VERSION_GREATER_EQUAL "3.18")
+  cmake_policy(SET CMP0104 OLD) # Can't get CUDA_ARCHITECTURES to work with NEW
+endif()
 
 # Set sketch version
 file(GLOB VERSION_FILES ${CMAKE_CURRENT_SOURCE_DIR}/*.cpp ${CMAKE_CURRENT_SOURCE_DIR}/src/sketch/*.hpp)
@@ -17,36 +20,48 @@ set(TARGET_NAME pp_sketchlib)
 add_compile_definitions(PYTHON_EXT)
 
 # gcc: Add openmp
-# gcc: Add -O0 to remove optimizations when using debug
-IF(CMAKE_COMPILER_IS_GNUCC)
+IF(CMAKE_COMPILER_IS_GNUCC OR "$ENV{SKETCHLIB_INSTALL}" STREQUAL "conda")
     set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fopenmp")
-    set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} -O0")
-    set(CMAKE_C_FLAGS_DEBUG "${CMAKE_C_FLAGS_DEBUG} -O0")
-ENDIF(CMAKE_COMPILER_IS_GNUCC)
+ENDIF()
+
+# Add -O0 to remove optimizations when using debug
+set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} -O0")
+set(CMAKE_C_FLAGS_DEBUG "${CMAKE_C_FLAGS_DEBUG} -O0")
 
 if(UNIX AND NOT APPLE)
     if(CMAKE_CXX_COMPILER STREQUAL "icpc")
-        set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fast -xCASCADELAKE -DMKL_ILP64 -m64 -static-intel")
+        set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fast -march=\"native\" -DMKL_ILP64 -m64 -static-intel")
     else()
         set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -D__STDC_LIMIT_MACROS -D__STDC_CONSTANT_MACROS")
         set(CMAKE_LD_FLAGS "${CMAKE_LDFLAGS} -Wl,--as-needed")
     endif()
 endif()
 
+set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall")
+set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} -ffast-math -funroll-loops -m64")
+
 # Set paths for non standard lib/ and include/ locations
 if(DEFINED ENV{CONDA_PREFIX})
     include_directories($ENV{CONDA_PREFIX}/include)
     link_directories($ENV{CONDA_PREFIX}/lib)
     link_directories($ENV{CONDA_PREFIX}/lib/intel64)
+else()
+    find_package(OpenMP)
 endif()
 
 # Add libraries
+
+find_package(HDF5 REQUIRED COMPONENTS CXX)
+include_directories(${HDF5_INCLUDE_DIRS})
+include_directories(${CMAKE_CURRENT_SOURCE_DIR}/vendor/highfive/include)
 include_directories(${CMAKE_CURRENT_SOURCE_DIR}/src)
-find_package(pybind11 REQUIRED)
-find_package (Eigen3 3.3 REQUIRED NO_MODULE)
+
+find_package(ZLIB)
+execute_process(COMMAND pybind11-config --cmakedir OUTPUT_STRIP_TRAILING_WHITESPACE OUTPUT_VARIABLE pybind11_DIR)
+find_package(pybind11 2.6 CONFIG REQUIRED)
+find_package(Eigen3 3.3 REQUIRED NO_MODULE)
 find_package(Armadillo REQUIRED)
 include_directories(${ARMADILLO_INCLUDE_DIRS})
-#find_package(OpenMP) # This links system openmp if present - conda sorts out rpath but take care
 
 # Define python library target
 add_library("${TARGET_NAME}" MODULE)
@@ -55,42 +70,46 @@ add_library("${TARGET_NAME}" MODULE)
 include(CheckLanguage)
 check_language(CUDA)
 if(CMAKE_CUDA_COMPILER)
-    message(STATUS "CUDA found, compiling both GPU and CPU code")
     enable_language(CUDA)
+    if (CMAKE_CUDA_COMPILER_VERSION VERSION_GREATER 11.0)
+        message(STATUS "CUDA >11.0 found, compiling both GPU and CPU code")
 
-    # PIC/relocatable-device-code needed as this is linked by gcc later
-    # -Xptxas -dlcm=ca turns cache on, but not needed in recent nvcc versions
-    # --cudart static: static linking of the CUDA libraries
-    # -gencode arch=compute_35 etc compiles for each (minimum) device version listed (v3.5, v5.0, v7.5)
-    set(CUDA_OPTS "-Xcompiler -fPIC -Xptxas -dlcm=ca --relocatable-device-code=true --expt-relaxed-constexpr")
-    # Turn on link time optimisation if available
-    if(CMAKE_CUDA_COMPILER_VERSION VERSION_GREATER 11.0 AND CMAKE_BUILD_TYPE MATCHES Release)
-        string(APPEND CUDA_OPTS " -dlto -arch=sm_86")
+        # PIC/relocatable-device-code needed as this is linked by gcc later
+        # -Xptxas -dlcm=ca turns cache on, but not needed in recent nvcc versions
+        # --cudart static: static linking of the CUDA libraries
+        # -gencode arch=compute_35 etc compiles for each (minimum) device version listed (v3.5, v5.0, v7.5)
+        set(CUDA_OPTS "-Xcompiler -fPIC -Xptxas -dlcm=ca --relocatable-device-code=true --expt-relaxed-constexpr")
+        # Turn on link time optimisation if available
+        if(CMAKE_BUILD_TYPE MATCHES Release)
+            string(APPEND CUDA_OPTS " -dlto -arch=sm_86")
+        else()
+            string(APPEND CUDA_OPTS " -gencode arch=compute_70,code=sm_70 -gencode arch=compute_75,code=sm_75 -gencode arch=compute_80,code=sm_80 -gencode arch=compute_86,code=sm_86")
+        endif()
+        if(CMAKE_BUILD_TYPE MATCHES Debug)
+            string(APPEND CUDA_OPTS " -G")
+        endif()
+
+        set(CMAKE_CUDA_FLAGS "${CUDA_OPTS}")
+
+        add_compile_definitions(GPU_AVAILABLE)
+        add_library("${TARGET_NAME}_CUDA" OBJECT src/gpu/dist.cu
+                                                src/gpu/sketch.cu
+                                                src/gpu/device_memory.cu
+                                                src/gpu/gpu_countmin.cu
+                                                src/gpu/device_reads.cu)
+        target_include_directories("${TARGET_NAME}_CUDA" PRIVATE "${EIGEN3_INCLUDE_DIR}" "${pybind11_INCLUDE_DIRS}")
+        set_property(TARGET "${TARGET_NAME}_CUDA"
+                    PROPERTY POSITION_INDEPENDENT_CODE ON
+                    CUDA_SEPARABLE_COMPILATION ON
+                    CUDA_RESOLVE_DEVICE_SYMBOLS ON   # try and ensure device link with nvcc
+                    CUDA_VISIBILITY_PRESET "hidden"
+                CUDA_RUNTIME_LIBRARY Static)
+              #CUDA_ARCHITECTURES OFF) # set off as done explicitly above (due to dlto complexities)
+        # CPU code/gcc compiled code needed by cuda lib
+        target_sources("${TARGET_NAME}" PRIVATE src/gpu/gpu_api.cpp)
     else()
-        string(APPEND CUDA_OPTS " -gencode arch=compute_70,code=sm_70 -gencode arch=compute_75,code=sm_75 -gencode arch=compute_80,code=sm_80 -gencode arch=compute_86,code=sm_86")
+        message(STATUS "CUDA >=11.0 required, compiling CPU code only")
     endif()
-    if(CMAKE_BUILD_TYPE MATCHES Debug)
-        string(APPEND CUDA_OPTS " -G")
-    endif()
-
-    set(CMAKE_CUDA_FLAGS "${CUDA_OPTS}")
-
-    add_compile_definitions(GPU_AVAILABLE)
-    add_library("${TARGET_NAME}_CUDA" OBJECT src/gpu/dist.cu
-                                             src/gpu/sketch.cu
-                                             src/gpu/device_memory.cu
-                                             src/gpu/gpu_countmin.cu
-                                             src/gpu/device_reads.cu)
-    target_include_directories("${TARGET_NAME}_CUDA" PRIVATE "${EIGEN3_INCLUDE_DIR}" "${pybind11_INCLUDE_DIRS}")
-    set_property(TARGET "${TARGET_NAME}_CUDA"
-                 PROPERTY POSITION_INDEPENDENT_CODE ON
-                 CUDA_SEPARABLE_COMPILATION ON
-                 CUDA_RESOLVE_DEVICE_SYMBOLS ON   # try and ensure device link with nvcc
-                 CUDA_VISIBILITY_PRESET "hidden"
-		         CUDA_RUNTIME_LIBRARY Static)
-	         #CUDA_ARCHITECTURES OFF) # set off as done explicitly above (due to dlto complexities)
-    # CPU code/gcc compiled code needed by cuda lib
-    target_sources("${TARGET_NAME}" PRIVATE src/gpu/gpu_api.cpp)
 else()
     message(STATUS "CUDA not found, compiling CPU code only")
 endif()
@@ -124,8 +143,12 @@ if(CMAKE_CUDA_COMPILER)
     set_property(TARGET "${TARGET_NAME}" PROPERTY CUDA_RESOLVE_DEVICE_SYMBOLS ON)
     #set_property(TARGET "${TARGET_NAME}" PROPERTY CUDA_ARCHITECTURES OFF)
 endif()
-target_link_libraries("${TARGET_NAME}" PRIVATE pybind11::module Eigen3::Eigen
-                                               z hdf5_cpp hdf5 gomp openblas lapack gfortran m dl)
-#if(OpenMP_CXX_FOUND)
-#    target_link_libraries("${TARGET_NAME}" PRIVATE OpenMP::OpenMP_CXX)
-#endif()
+target_link_libraries("${TARGET_NAME}" PRIVATE pybind11::module Eigen3::Eigen ${HDF5_LIBRARIES} openblas lapack gfortran m dl)
+if(DEFINED ENV{CONDA_PREFIX} AND (NOT APPLE OR CMAKE_COMPILER_IS_GNUCC OR ENV{SKETCHLIB_INSTALL} EQUAL "conda"))
+    target_link_libraries("${TARGET_NAME}" PRIVATE gomp z)
+else()
+    target_link_libraries("${TARGET_NAME}" PRIVATE ZLIB::ZLIB)
+    if(OpenMP_CXX_FOUND)
+        target_link_libraries("${TARGET_NAME}" PRIVATE OpenMP::OpenMP_CXX)
+    endif()
+endif()
diff --git a/LICENSE_highfive b/LICENSE_highfive
new file mode 100644
index 00000000..bc1edcab
--- /dev/null
+++ b/LICENSE_highfive
@@ -0,0 +1,25 @@
+Boost Software License - Version 1.0 - August 17th, 2003
+
+Permission is hereby granted, free of charge, to any person or organization
+obtaining a copy of the software and accompanying documentation covered by
+this license (the "Software") to use, reproduce, display, distribute,
+execute, and transmit the Software, and to prepare derivative works of the
+Software, and to permit third-parties to whom the Software is furnished to
+do so, all subject to the following:
+
+The copyright notices in the Software and this entire statement, including
+the above license grant, this restriction and the following disclaimer,
+must be included in all copies of the Software, in whole or in part, and
+all derivative works of the Software, unless such copies or derivative
+works are solely in the form of machine-executable object code generated by
+a source language processor.
+
+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, TITLE AND NON-INFRINGEMENT. IN NO EVENT
+SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE
+FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE,
+ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+DEALINGS IN THE SOFTWARE.
+
+
diff --git a/MANIFEST.in b/MANIFEST.in
new file mode 100644
index 00000000..939a35e8
--- /dev/null
+++ b/MANIFEST.in
@@ -0,0 +1,3 @@
+include CMakeLists.txt
+recursive-include src *
+recursive-include vendor *
diff --git a/README.md b/README.md
index 7aec63c0..0feb62ab 100644
--- a/README.md
+++ b/README.md
@@ -1,7 +1,8 @@
 # pp-sketchlib <img src='sketchlib_logo.png' align="right" height="139" />
 
 <!-- badges: start -->
-[![Build status](https://dev.azure.com/jlees/pp-sketchlib/_apis/build/status/johnlees.pp-sketchlib?branchName=master)](https://dev.azure.com/jlees/pp-sketchlib/_build/latest?definitionId=1&branchName=master)
+[![Build Status](https://dev.azure.com/jlees/pp-sketchlib/_apis/build/status/bacpop.pp-sketchlib?branchName=master)](https://dev.azure.com/jlees/pp-sketchlib/_build/latest?definitionId=4&branchName=master)
+[![Build status](https://badge.buildkite.com/b1bc9ccd16211ca5a55846b95e297554e5aa3b544d8cb752b0.svg?branch=master;theme=github)](https://buildkite.com/mrc-ide/pp-sketchlib)
 [![Anaconda package](https://anaconda.org/conda-forge/pp-sketchlib/badges/version.svg)](https://anaconda.org/conda-forge/pp-sketchlib)
 <!-- badges: end -->
 
@@ -9,6 +10,9 @@
 Library of sketching functions used by [PopPUNK](https://www.poppunk.net>). See documentation at http://poppunk.readthedocs.io/en/latest/sketching.html
 
 ## Installation
+
+### conda
+
 Install using conda (recommended):
 
 ```
@@ -21,6 +25,25 @@ conda install -c conda-forge pp-sketchlib
     It may also help if you downgrade your version of conda (to 4.5). Installing into
     a new environment is recommended.
 
+### pip
+
+Or install through pip
+
+You need to have suitable system dependencies installed.  On ubuntu, this suffices:
+
+```
+apt-get update && apt-get install -y --no-install-recommends \
+  cmake gfortran libarmadillo-dev libeigen3-dev libopenblas-dev
+```
+
+Then install pp-sketchlib via pip:
+
+```
+pip3 install --user pp-sketchlib
+```
+
+### local (build w/ compile)
+
 Or install locally:
 
 ```
@@ -40,6 +63,7 @@ For this option you will need (all available through conda):
 If you wish to compile the GPU code you will also need the CUDA toolkit
 installed (tested on 10.2 and 11.0).
 
+
 ## Usage
 Create a set of sketches and save these as a database:
 
@@ -86,19 +110,17 @@ When working with large datasets, you can increase the `--cpus` to high numbers
 a roughly proportional performance increase.
 
 For calculating sketches of read datasets, or large numbers of distances, and you have a CUDA compatible GPU,
-you can calculate distances on your graphics device even more quickly. Add the `--use-gpu` option:
+you can calculate distances on your graphics device even more quickly. Add the `--gpu` option with the desired
+device ID:
 
 ```
-poppunk_sketch --sketch --rfile rfiles.txt --ref-db listeria --cpus 4 --use-gpu
-poppunk_sketch --query --ref-db listeria --query-db listeria --use-gpu
+sketchlib sketch -l rfiles.txt -o listeria --cpus 4 --gpu 0
+sketchlib query dist listeria --gpu 0
 ```
 
 Both CPU parallelism and the GPU will be used, so be sure to add
-both `--cpus` and `--use-gpu` for maximum speed. This is particularly efficient
-when sketching.
-
-You can set the `--gpu-id` if you have more than one device, which may be necessary on
-cluster systems. This mode can also benefit from having multiple CPU cores available too.
+both `--cpus` and `--gpu` for maximum speed. This is particularly efficient
+when sketching reads.
 
 ### Benchmarks
 
@@ -332,3 +354,54 @@ Modifiers:
 - `DEBUG=1` runs with debug flags
 - `PROFILE=1` runs with profiler flags for `ncu` and `nsys`
 - `GPU=1` also build CUDA code (assumes `/usr/local/cuda-11.1/` and SM v8.6)
+
+### azure
+The repository key for the ubuntu CUDA install is periodically updated, which may cause build failures. See https://developer.nvidia.com/blog/updating-the-cuda-linux-gpg-repository-key/ and update in `azure-pipelines.yml`.
+
+### Test that Python can build an installable package
+
+Build a python source package and install it into an empty docker container with vanilla python 3. If this works, then there's a good chance that the version uploaded to pypi will work
+
+```
+rm -rf dist
+python3 setup.py sdist
+docker run --rm -it -v "${PWD}:/src:ro" python:3 /src/docker/install
+```
+
+See [this PR](https://github.com/bacpop/pp-sketchlib/pull/70) for the sorts of things we're trying to work around here.
+
+### Publish to pypi
+
+If things are being weird, the test index can be useful:
+
+```
+python3 setup.py sdist
+twine upload --repository testpypi dist/*
+```
+
+You can test installing this into an empty docker container with
+
+```
+docker run --rm -it --entrypoint bash python:3
+apt-get update && apt-get install -y --no-install-recommends \
+  cmake gfortran libarmadillo-dev libeigen3-dev libopenblas-dev
+pip install -i https://test.pypi.org/simple/ \
+  --extra-index-url https://pypi.org/simple \
+  pp-sketchlib
+```
+
+It can take a few minutes for the new version to become available so you may want to do
+
+```
+pip install -i https://test.pypi.org/simple/ \
+  --extra-index-url https://pypi.org/simple \
+  pp-sketchlib==1.7.5.3
+```
+
+updated with your current version to force installation of the new one.
+
+Once satisfied that pip/twine haven't uploaded a completely broken package (and typically once the PR is merged) upload to the main pypi.
+
+```
+twine upload dist/*
+```
diff --git a/azure-pipelines.yml b/azure-pipelines.yml
index 5cf16a93..4e6bf1e0 100644
--- a/azure-pipelines.yml
+++ b/azure-pipelines.yml
@@ -29,13 +29,13 @@ steps:
 - script: |
     wget https://developer.download.nvidia.com/compute/cuda/repos/ubuntu1804/x86_64/cuda-ubuntu1804.pin
     sudo mv cuda-ubuntu1804.pin /etc/apt/preferences.d/cuda-repository-pin-600
-    sudo apt-key adv --fetch-keys https://developer.download.nvidia.com/compute/cuda/repos/ubuntu1804/x86_64/7fa2af80.pub
+    sudo apt-key adv --fetch-keys https://developer.download.nvidia.com/compute/cuda/repos/ubuntu1804/x86_64/3bf863cc.pub
     sudo add-apt-repository "deb https://developer.download.nvidia.com/compute/cuda/repos/ubuntu1804/x86_64/ /"
     sudo apt-get update
     sudo apt-get -y install cuda=11.2.2-1
   displayName: Install nvcc
 
-- bash: conda env create --quiet --file environment.yml
+- bash: conda env create --file environment.yml
   displayName: Create Anaconda environment and install dependencies
 
 # to add C++ tests add the following line, and remove --no-cpp
diff --git a/buildkite/pipeline.yml b/buildkite/pipeline.yml
new file mode 100644
index 00000000..05b60dba
--- /dev/null
+++ b/buildkite/pipeline.yml
@@ -0,0 +1,13 @@
+steps:
+  - label: ":whale::python: Build"
+    command: docker/build
+
+  - wait
+
+  - label: ":hammer: Test image"
+    command: docker/test
+
+  - wait
+
+  - label: ":shipit: Push images"
+    command: docker/push
diff --git a/docker/Dockerfile b/docker/Dockerfile
new file mode 100644
index 00000000..c4f4b314
--- /dev/null
+++ b/docker/Dockerfile
@@ -0,0 +1,16 @@
+FROM python:3
+
+RUN apt-get update && \
+    apt-get install -y --no-install-recommends \
+                          cmake \
+                          gfortran \
+                          libarmadillo-dev \
+                          libeigen3-dev \
+                          libopenblas-dev
+
+COPY requirements.txt /tmp/requirements.txt
+RUN pip install -r /tmp/requirements.txt && rm -f /tmp/requirements.txt
+
+COPY . /src
+WORKDIR /src
+RUN python setup.py install
diff --git a/docker/build b/docker/build
new file mode 100755
index 00000000..d91bf1b6
--- /dev/null
+++ b/docker/build
@@ -0,0 +1,14 @@
+#!/usr/bin/env bash
+set -ex
+
+HERE=$(dirname $0)
+. $HERE/common
+
+docker build --pull \
+       --tag $TAG_SHA \
+       -f docker/Dockerfile \
+       $PACKAGE_ROOT
+
+# We always push the SHA tagged versions, for debugging if the tests
+# after this step fail
+docker push $TAG_SHA
diff --git a/docker/common b/docker/common
new file mode 100755
index 00000000..8f9947f1
--- /dev/null
+++ b/docker/common
@@ -0,0 +1,25 @@
+#!/usr/bin/env bash
+set -e
+
+PACKAGE_ROOT=$(realpath $HERE/..)
+PACKAGE_NAME=pp-sketchlib
+PACKAGE_ORG=mrcide
+
+# Buildkite doesn't check out a full history from the remote (just the
+# single commit) so you end up with a detached head and git rev-parse
+# doesn't work
+if [ "$BUILDKITE" = "true" ]; then
+    GIT_SHA=${BUILDKITE_COMMIT:0:7}
+else
+    GIT_SHA=$(git -C "$PACKAGE_ROOT" rev-parse --short=7 HEAD)
+fi
+
+if [ "$BUILDKITE" = "true" ]; then
+    GIT_BRANCH=$BUILDKITE_BRANCH
+else
+    GIT_BRANCH=$(git -C "$PACKAGE_ROOT" symbolic-ref --short HEAD)
+fi
+
+TAG_SHA="${PACKAGE_ORG}/${PACKAGE_NAME}:${GIT_SHA}"
+TAG_BRANCH="${PACKAGE_ORG}/${PACKAGE_NAME}:${GIT_BRANCH}"
+TAG_LATEST="${PACKAGE_ORG}/${PACKAGE_NAME}:latest"
diff --git a/docker/install b/docker/install
new file mode 100755
index 00000000..b58f13b7
--- /dev/null
+++ b/docker/install
@@ -0,0 +1,15 @@
+#!/usr/bin/env bash
+set -eux
+
+SRC="${1:-/src/dist}"
+TGZ=$(ls -1 $SRC/*.tar.gz | tail -n1)
+
+apt-get update && \
+    apt-get install -y --no-install-recommends \
+            cmake \
+            gfortran \
+            libarmadillo-dev \
+            libeigen3-dev \
+            libopenblas-dev
+
+pip install "$TGZ"
diff --git a/docker/push b/docker/push
new file mode 100755
index 00000000..103533a1
--- /dev/null
+++ b/docker/push
@@ -0,0 +1,16 @@
+#!/usr/bin/env bash
+set -e
+
+HERE=$(dirname $0)
+. $HERE/common
+
+# In case we switch agents between steps
+[ ! -z $(docker images -q $TAG_SHA) ] || docker pull $TAG_SHA
+
+docker tag $TAG_SHA $TAG_BRANCH
+docker push $TAG_BRANCH
+
+if [ $GIT_BRANCH == "master" ]; then
+   docker tag $TAG_SHA $TAG_LATEST
+   docker push $TAG_LATEST
+fi
diff --git a/pp_sketch/__main__.py b/pp_sketch/__main__.py
index ea4099e9..825f9757 100644
--- a/pp_sketch/__main__.py
+++ b/pp_sketch/__main__.py
@@ -4,9 +4,10 @@
 Usage:
   sketchlib sketch <files>... -o <output> [-k <kseq>|--kmer <k>] [-s <size>] [--single-strand] [--codon-phased] [--min-count <count>] [--exact-counter] [--cpus <cpus>] [--gpu <gpu>]
   sketchlib sketch -l <file-list> -o <output> [-k <kseq>|--kmer <k>] [-s <size>] [--single-strand] [--codon-phased] [--min-count <count>] [--exact-counter] [--cpus <cpus>] [--gpu <gpu>]
-  sketchlib query dist <db1> [<db2>] [-o <output>] [--adj-random] [--cpus <cpus>] [--gpu <gpu>]
-  sketchlib query jaccard <db1> [<db2>] [-o <output>] [--kmer <k>] [--adj-random] [--subset <file>] [--cpus <cpus>] [--gpu <gpu>]
-  sketchlib query sparse <db1> [<db2>] (--kNN <k>|--threshold <max>) [-o <output>] [--accessory] [--adj-random] [--subset <file>] [--cpus <cpus>] [--gpu <gpu>]
+  sketchlib query dist <db1> [<db2>] [-o <output>] [--adj-random] [--subset <file>] [--cpus <cpus>] [--gpu <gpu>]
+  sketchlib query jaccard <db1> [<db2>] [-o <output>] [--kmer <k>] [--adj-random] [--subset <file>] [--cpus <cpus>]
+  sketchlib query sparse <db1> (--kNN <k>|--threshold <max>) [-o <output>] [--accessory] [--adj-random] [--subset <file>] [--cpus <cpus>] [--gpu <gpu>]
+  sketchlib query sparse jaccard <db1> --kNN <k> --kmer <k> [-o <output>] [--adj-random] [--subset <file>] [--cpus <cpus>]
   sketchlib join <db1> <db2> -o <output>
   sketchlib (add|remove) random <db1> [--cpus <cpus>]
   sketchlib (-h | --help)
@@ -22,7 +23,7 @@
   --gpu <gpu>    Use GPU with specified device ID [default: -1].
 
   -k <kseq>     Sequence of k-mers to sketch (min,max,step) [default: 15,31,4].
-  --kmer <k>    Sketch at a single k-mer length k.
+  --kmer <k>    Sketch (or distance) at a single k-mer length k.
   -s <size>     Sketch size [default: 10000].
   --single-strand  Ignore the reverse complement (e.g. in RNA viruses).
   --codon-phased  Use codon phased seeds X--X--X
@@ -45,7 +46,6 @@
 import pickle
 import h5py
 
-#sys.path.insert(0, '/Users/jlees/Documents/Imperial/pp-sketchlib/build/lib.macosx-10.9-x86_64-3.8')
 import pp_sketchlib
 
 from .matrix import ijv_to_coo
@@ -130,7 +130,7 @@ def get_options():
             if min_k >= max_k or min_k < 3 or max_k > 101 or k_step < 1:
                 raise RuntimeError("Invalid k-mer sizes")
             arguments['kmers'] = np.arange(int(min_k), int(max_k) + 1, int(k_step))
-        except:
+        except RuntimeError:
             sys.stderr.write("Minimum kmer size must be smaller than maximum kmer size; " +
                              "range must be between 3 and 101, step must be at least one\n")
             sys.exit(1)
@@ -183,19 +183,19 @@ def main():
                              "All names must be unique\n")
             sys.exit(1)
 
-        pp_sketchlib.constructDatabase(args['-o'],
-                                       names,
-                                       sequences,
-                                       args['kmers'],
-                                       args['-s'],
-                                       args['--codon-phased'],
-                                       False,
-                                       not args['--single-strand'],
-                                       args['--min-count'],
-                                       args['--exact-counter'],
-                                       args['--cpus'],
-                                       args['--use-gpu'],
-                                       args['--gpu'])
+        pp_sketchlib.constructDatabase(db_name=args['-o'],
+                                       samples=names,
+                                       files=sequences,
+                                       klist=args['kmers'],
+                                       sketch_size=args['-s'],
+                                       codon_phased=args['--codon-phased'],
+                                       calc_random=False,
+                                       use_rc=not args['--single-strand'],
+                                       min_count=args['--min-count'],
+                                       exact=args['--exact-counter'],
+                                       num_threads=args['--cpus'],
+                                       use_gpu=args['--use-gpu'],
+                                       device_id=args['--gpu'])
 
     #
     # Join two databases
@@ -220,6 +220,8 @@ def main():
                 sys.stderr.write("One database uses codon-phased seeds - cannot join "
                                  "with a standard seed database\n")
         except RuntimeError as e:
+            hdf1.close()
+            hdf2.close()
             sys.stderr.write("Unable to check sketch version\n")
 
         hdf_join = h5py.File(join_name + ".tmp", 'w') # add .tmp in case join_name exists
@@ -236,17 +238,15 @@ def main():
                 sys.stderr.write("Random matches found in one database, which will not be copied\n"
                                  "Use --add-random to recalculate for the joined DB\n")
         except RuntimeError as e:
-            hdf1.close()
-            hdf2.close()
-            hdf_join.close()
             sys.stderr.write("ERROR: " + str(e) + "\n")
             sys.stderr.write("Joining sketches failed\n")
             sys.exit(1)
+        finally:
+            hdf1.close()
+            hdf2.close()
+            hdf_join.close()
 
         # Clean up
-        hdf1.close()
-        hdf2.close()
-        hdf_join.close()
         os.rename(join_name + ".tmp", join_name)
 
     #
@@ -274,36 +274,71 @@ def main():
                 sys.exit(1)
 
         # Check inputs overlap
-        ref = h5py.File(args['db1'] + ".h5", 'r')
-        query = h5py.File(args['db2'] + ".h5", 'r')
-        db_kmers = set(ref['sketches/' + rList[0]].attrs['kmers']).intersection(
-           query['sketches/' + qList[0]].attrs['kmers']
-        )
-        query_kmers = sorted(db_kmers)
-        if args['jaccard'] and len(args['kmers']) == 1:
-            if args['kmers'][0] not in query_kmers:
-                sys.stderr.write("Selected --kmer is not in both query databases\n")
-                sys.exit(1)
-            else:
-                query_kmers = args['kmers']
-        ref.close()
-        query.close()
+        try:
+            ref = h5py.File(args['db1'] + ".h5", 'r')
+            query = h5py.File(args['db2'] + ".h5", 'r')
+            db_kmers = set(ref['sketches/' + rList[0]].attrs['kmers']).intersection(
+              query['sketches/' + qList[0]].attrs['kmers']
+            )
+            query_kmers = sorted(db_kmers)
+            if args['jaccard'] and len(args['kmers']) == 1:
+                if args['kmers'][0] not in query_kmers:
+                    raise RuntimeError(f"Input --kmer {args['kmers'][0]} not in "
+                                       f"(both) databases: {sorted(db_kmers)}\n")
+                else:
+                    query_kmers = args['kmers']
+            elif not query_kmers:
+                raise RuntimeError("No overlapping k-mers in db1 and db2")
+        finally:
+            ref.close()
+            query.close()
 
         if args['sparse']:
-            sparseIdx = pp_sketchlib.queryDatabaseSparse(args['db1'],
-                                                         args['db2'],
-                                                         rList,
-                                                         qList,
-                                                         query_kmers,
-                                                         args['--adj-random'],
-                                                         args['--threshold'],
-                                                         args['--kNN'],
-                                                         not args['--accessory'],
-                                                         args['--cpus'],
-                                                         args['--use-gpu'],
-                                                         args['--gpu'])
+            # Set which distance to output (sparse only supports a single value)
+            dist_col = 0
+            if args['jaccard'] and len(query_kmers) != 1:
+                # Note default here is just to be sending a single-kmer length, so
+                # it does not need to be indexed into hence dist_col = 0 is correct
+                raise RuntimeError(f"For sparse jaccard a single k-mer must be "
+                                   f"selected {sorted(query_kmers)}")
+            elif args['--accessory']:
+                dist_col = 1
+
+            # Can use memory efficient version with kNN
+            if args['--threshold'] == 0:
+                sparseIdx = pp_sketchlib.querySelfSparse(ref_db_name=args['db1'],
+                                              rList=rList,
+                                              klist=query_kmers,
+                                              random_correct=args['--adj-random'],
+                                              jaccard=args['jaccard'],
+                                              kNN=args['--kNN'],
+                                              dist_cutoff=0,
+                                              dist_col=dist_col,
+                                              num_threads=args['--cpus'],
+                                              use_gpu=args['--use-gpu'],
+                                              device_id=args['--gpu'])
+            # Otherwise use general version (potentially large memory use as it
+            # actually calculates the dense distances, then sparsifies them)
+            else:
+                # This should be prohibited by docopt, but just in case
+                if args['jaccard']:
+                    raise ValueError("Cannot use sparse jaccard with non-self "
+                                     "or --threshold")
+                sparseIdx = pp_sketchlib.querySelfSparse(ref_db_name=args['db1'],
+                              rList=rList,
+                              klist=query_kmers,
+                              random_correct=args['--adj-random'],
+                              jaccard=args['jaccard'],
+                              kNN=0,
+                              dist_cutoff=args['--threshold'],
+                              dist_col=dist_col,
+                              num_threads=args['--cpus'],
+                              use_gpu=args['--use-gpu'],
+                              device_id=args['--gpu'])
             if not args['-o']:
-                if args['--accessory']:
+                if args['jaccard']:
+                    distName = str(query_kmers[0])
+                elif args['--accessory']:
                     distName = 'Accessory'
                 else:
                     distName = 'Core'
@@ -318,16 +353,16 @@ def main():
                 storePickle(rList, qList, rList == qList, coo_matrix, args['-o'])
 
         else:
-            distMat = pp_sketchlib.queryDatabase(args['db1'],
-                                                 args['db2'],
-                                                 rList,
-                                                 qList,
-                                                 query_kmers,
-                                                 args['--adj-random'],
-                                                 args['jaccard'],
-                                                 args['--cpus'],
-                                                 args['--use-gpu'],
-                                                 args['--gpu'])
+            distMat = pp_sketchlib.queryDatabase(ref_db_name=args['db1'],
+                                                 query_db_name=args['db2'],
+                                                 rList=rList,
+                                                 qList=qList,
+                                                 klist=query_kmers,
+                                                 random_correct=args['--adj-random'],
+                                                 jaccard=args['jaccard'],
+                                                 num_threads=args['--cpus'],
+                                                 use_gpu=args['--use-gpu'],
+                                                 device_id=args['--gpu'])
 
             # get names order
             if not args['-o']:
@@ -353,11 +388,11 @@ def main():
             db_kmers = ref['sketches/' + rList[0]].attrs['kmers']
             ref.close()
 
-            pp_sketchlib.addRandom(args['db1'],
-                                   rList,
-                                   db_kmers,
-                                   not args['--single-strand'],
-                                   args['--cpus'])
+            pp_sketchlib.addRandom(db_name=args['db1'],
+                                   samples=rList,
+                                   klist=db_kmers,
+                                   use_rc=not args['--single-strand'],
+                                   num_threads=args['--cpus'])
         elif args['remove']:
             if 'random' in ref:
                 del ref['random']
diff --git a/pyproject.toml b/pyproject.toml
new file mode 100644
index 00000000..20bf631f
--- /dev/null
+++ b/pyproject.toml
@@ -0,0 +1,3 @@
+[build-system]
+requires = ["setuptools>=42", "wheel", "pybind11~=2.6.1"]
+build-backend = "setuptools.build_meta"
diff --git a/requirements.txt b/requirements.txt
new file mode 100644
index 00000000..398ec6ce
--- /dev/null
+++ b/requirements.txt
@@ -0,0 +1,5 @@
+h5py
+numpy
+pybind11
+scipy
+docopt
diff --git a/scripts/update_highfive.py b/scripts/update_highfive.py
new file mode 100755
index 00000000..6c6bfe5c
--- /dev/null
+++ b/scripts/update_highfive.py
@@ -0,0 +1,13 @@
+#!/usr/bin/env bash
+set -eu
+HERE="$(dirname $0)"
+ROOT="$(realpath $HERE/..)"
+DEST="${ROOT}/vendor/highfive"
+HIGHFIVE_VERSION="v2.3.1"
+HIGHFIVE_URL="https://github.com/BlueBrain/HighFive/archive/refs/tags/${HIGHFIVE_VERSION}.tar.gz"
+echo "Downloading highfive ${HIGHFIVE_VERSION} to ${DEST}"
+rm -rf "$DEST"
+mkdir -p "$DEST"
+curl --silent -L "$HIGHFIVE_URL" | tar zxf - -C "$DEST" --strip-components=1
+cp $DEST/LICENSE $ROOT/LICENSE_highfive
+echo "Done!"
diff --git a/setup.py b/setup.py
old mode 100644
new mode 100755
index fe2cfd9c..da41f68e
--- a/setup.py
+++ b/setup.py
@@ -123,9 +123,11 @@ def build_extension(self, ext):
             'sketchlib = pp_sketch.__main__:main'
             ]
     },
+    setup_requires=['pybind11'],
     install_requires=['numpy',
                       'scipy',
-                      'h5py'],
+                      'h5py',
+                      'pybind11'],
     ext_modules=[CMakeExtension('pp_sketchlib')],
     cmdclass=dict(build_ext=CMakeBuild),
     zip_safe=False
diff --git a/src/api.cpp b/src/api.cpp
index c8bee289..3219f044 100644
--- a/src/api.cpp
+++ b/src/api.cpp
@@ -16,12 +16,11 @@
 #include "gpu/gpu.hpp"
 #include "reference.hpp"
 #include "sketch/progress.hpp"
+#include "sketch/bitfuncs.hpp"
 
 using namespace Eigen;
 namespace py = pybind11;
 
-const int progressBitshift = 10;
-
 bool same_db_version(const std::string &db1_name, const std::string &db2_name) {
   // Open databases
   Database db1(db1_name + ".h5");
@@ -77,6 +76,7 @@ std::vector<Reference> create_sketches(
     KmerSeeds kmer_seeds = generate_seeds(kmer_lengths, codon_phased);
 
     ProgressMeter sketch_progress(names.size());
+    size_t done_count = 0;
     bool interrupt = false;
     std::vector<std::runtime_error> errors;
 #pragma omp parallel for schedule(dynamic, 5) num_threads(num_threads)
@@ -88,6 +88,8 @@ std::vector<Reference> create_sketches(
           SeqBuf seq_in(files[i], kmer_lengths.back());
           sketches[i] = Reference(names[i], seq_in, kmer_seeds, sketchsize64,
                                   codon_phased, use_rc, min_count, exact);
+#pragma omp atomic
+          ++done_count;
         } catch (const std::runtime_error &e) {
 #pragma omp critical
           {
@@ -98,7 +100,7 @@ std::vector<Reference> create_sketches(
       }
 
       if (omp_get_thread_num() == 0) {
-        sketch_progress.tick(num_threads);
+        sketch_progress.tick_count(done_count);
       }
     }
     sketch_progress.finalise();
@@ -167,28 +169,32 @@ NumpyMatrix query_db(std::vector<Reference> &ref_sketches,
     dist_cols = 2;
   }
 
+  size_t dist_rows;
+  // Set up progress meter
+  if (ref_sketches == query_sketches) {
+    // calculate dists
+    dist_rows = static_cast<size_t>(0.5 * (ref_sketches.size()) *
+                                           (ref_sketches.size() - 1));
+  } else {
+    dist_rows = ref_sketches.size() * query_sketches.size();
+  }
+  static const uint64_t n_progress_ticks = 1000;
+  uint64_t update_every = 1;
+  if (dist_rows > n_progress_ticks) {
+    update_every = dist_rows / n_progress_ticks;
+  }
+  ProgressMeter dist_progress(n_progress_ticks, true);
+  volatile int progress = 0;
+
   // These could be the same but out of order, which could be dealt with
   // using a sort, except the return order of the distances wouldn't be as
   // expected. self iff ref_names == query_names as input
   bool interrupt = false;
   if (ref_sketches == query_sketches) {
     // calculate dists
-    size_t dist_rows = static_cast<size_t>(0.5 * (ref_sketches.size()) *
-                                           (ref_sketches.size() - 1));
     distMat.resize(dist_rows, dist_cols);
-
     arma::mat kmer_mat = kmer2mat<std::vector<size_t>>(kmer_lengths);
 
-    // Set up progress meter
-    size_t progress_blocks = 1 << progressBitshift;
-    size_t update_every = dist_rows >> progressBitshift;
-    if (progress_blocks > dist_rows || update_every < 1) {
-      progress_blocks = dist_rows;
-      update_every = 1;
-    }
-    ProgressMeter dist_progress(progress_blocks, true);
-    int progress = 0;
-
     // Iterate upper triangle
 #pragma omp parallel for schedule(dynamic, 5) num_threads(num_threads) shared(progress)
     for (size_t i = 0; i < ref_sketches.size(); i++) {
@@ -209,18 +215,18 @@ NumpyMatrix query_db(std::vector<Reference> &ref_sketches,
                     ref_sketches[j], kmer_mat, random_chance);
           }
           if (pos % update_every == 0) {
-#pragma omp atomic
-              progress++;
-              dist_progress.tick(1);
+#pragma omp critical
+              {
+                progress += MAX(1, n_progress_ticks / dist_rows);
+                dist_progress.tick_count(progress);
+              }
           }
         }
       }
     }
-    dist_progress.finalise();
   } else {
     // If ref != query, make a thread queue, with each element one ref
     // calculate dists
-    size_t dist_rows = ref_sketches.size() * query_sketches.size();
     distMat.resize(dist_rows, dist_cols);
 
     // Prepare objects used in distance calculations
@@ -235,7 +241,6 @@ NumpyMatrix query_db(std::vector<Reference> &ref_sketches,
           random_chance.closest_cluster(query_sketches[q_idx]);
     }
 
-    ProgressMeter dist_progress(dist_rows, true);
 #pragma omp parallel for collapse(2) schedule(static) num_threads(num_threads)
     for (unsigned int q_idx = 0; q_idx < query_sketches.size(); q_idx++) {
       for (unsigned int r_idx = 0; r_idx < ref_sketches.size(); r_idx++) {
@@ -260,23 +265,144 @@ NumpyMatrix query_db(std::vector<Reference> &ref_sketches,
                 query_sketches[q_idx].core_acc_dist<std::vector<double>>(
                     ref_sketches[r_idx], kmer_mat, jaccard_random);
           }
-          if (omp_get_thread_num() == 0) {
-            dist_progress.tick(num_threads);
+          if ((q_idx * ref_sketches.size() + r_idx) % update_every == 0) {
+#pragma omp critical
+            {
+              progress += MAX(1, n_progress_ticks / dist_rows);
+              dist_progress.tick_count(progress);
+            }
           }
         }
       }
     }
-    dist_progress.finalise();
   }
 
   // Handle Ctrl-C from python
   if (interrupt) {
     throw py::error_already_set();
   }
+  dist_progress.finalise();
 
   return (distMat);
 }
 
+void check_sparse_inputs(const std::vector<Reference> &ref_sketches,
+                         const std::vector<size_t> &kmer_lengths,
+                         RandomMC &random_chance, const bool jaccard,
+                         const size_t dist_col) {
+  if (ref_sketches.size() < 1) {
+    throw std::runtime_error("Query with empty ref or query list!");
+  }
+  if (kmer_lengths[0] <
+      random_chance.min_supported_k(ref_sketches[0].seq_length())) {
+    throw std::runtime_error("Smallest k-mer has no signal above random "
+                             "chance; increase minimum k-mer length");
+  }
+  if ((jaccard && dist_col >= kmer_lengths.size()) || (dist_col != 0 && dist_col != 1)) {
+    throw std::runtime_error("dist_col out of range");
+  }
+
+  // Check all references are in the random object, add if not
+  if (random_chance.check_present(ref_sketches, true)) {
+    std::cerr
+        << "Some members of the reference database were not found "
+           "in its random match chances. Consider refreshing with addRandom"
+        << std::endl;
+  }
+}
+
+sparse_coo query_db_sparse(std::vector<Reference> &ref_sketches,
+                     const std::vector<size_t> &kmer_lengths,
+                     RandomMC &random_chance, const bool jaccard,
+                     const int kNN, const size_t dist_col,
+                     const size_t num_threads) {
+  check_sparse_inputs(ref_sketches, kmer_lengths, random_chance,
+                      jaccard, dist_col);
+
+  std::cerr << "Calculating distances using " << num_threads << " thread(s)"
+            << std::endl;
+
+  std::vector<float> dists(ref_sketches.size() * kNN);
+  std::vector<long> i_vec(ref_sketches.size() * kNN);
+  std::vector<long> j_vec(ref_sketches.size() * kNN);
+
+  bool interrupt = false;
+
+  // Set up progress meter
+  size_t dist_rows = static_cast<size_t>(ref_sketches.size() * ref_sketches.size());
+  static const uint64_t n_progress_ticks = 1000;
+  uint64_t update_every = 1;
+  if (dist_rows > n_progress_ticks) {
+    update_every = dist_rows / n_progress_ticks;
+  }
+  ProgressMeter dist_progress(n_progress_ticks, true);
+  volatile int progress = 0;
+
+  arma::mat kmer_mat = kmer2mat<std::vector<size_t>>(kmer_lengths);
+#pragma omp parallel for schedule(static) num_threads(num_threads) shared(progress)
+  for (size_t i = 0; i < ref_sketches.size(); i++) {
+    std::vector<float> row_dists(ref_sketches.size());
+    if (interrupt || PyErr_CheckSignals() != 0) {
+      interrupt = true;
+    } else {
+      for (size_t j = 0; j < ref_sketches.size(); j++) {
+        if (i != j) {
+          if (jaccard) {
+            // Need 1-J here to sort correctly
+            row_dists[j] = 1.0f - ref_sketches[i].jaccard_dist(
+                ref_sketches[j], kmer_lengths[dist_col], random_chance);
+          } else {
+            float core, acc;
+            std::tie(core, acc) =
+                ref_sketches[i].core_acc_dist<RandomMC>(
+                    ref_sketches[j], kmer_mat, random_chance);
+            if (dist_col == 0) {
+              row_dists[j] = core;
+            } else {
+              row_dists[j] = acc;
+            }
+          }
+        } else {
+          row_dists[j] = std::numeric_limits<float>::infinity();
+        }
+        if ((i * ref_sketches.size() + j) % update_every == 0) {
+#pragma omp critical
+          {
+            progress += MAX(1, n_progress_ticks / dist_rows);
+            dist_progress.tick_count(progress);
+          }
+        }
+        long offset = i * kNN;
+        std::vector<long> ordered_dists = sort_indexes(row_dists);
+        std::fill_n(i_vec.begin() + offset, kNN, i);
+        // std::copy_n(ordered_dists.begin(), kNN, j_vec.begin() + offset);
+
+        for (int k = 0; k < kNN; ++k) {
+          j_vec[offset + k] = ordered_dists[k];
+          dists[offset + k] = row_dists[ordered_dists[k]];
+        }
+
+      }
+    }
+  }
+  dist_progress.finalise();
+
+  // Handle Ctrl-C from python
+  if (interrupt) {
+    throw py::error_already_set();
+  }
+
+  // Revert to correct distance J = 1 - dist
+  if (jaccard) {
+    #pragma omp parallel for simd num_threads(num_threads)
+    for (size_t i = 0; i < dists.size(); ++i) {
+      dists[i] = 1.0f - dists[i];
+    }
+  }
+
+  return (std::make_tuple(i_vec, j_vec, dists));
+}
+
 // Load sketches from a HDF5 file
 // Returns empty vector on failure
 std::vector<Reference> load_sketches(const std::string &db_name,
diff --git a/src/api.hpp b/src/api.hpp
index 85741424..ac12cdf0 100644
--- a/src/api.hpp
+++ b/src/api.hpp
@@ -40,6 +40,12 @@ NumpyMatrix query_db(std::vector<Reference> &ref_sketches,
                      const bool jaccard,
                      const size_t num_threads);
 
+sparse_coo query_db_sparse(std::vector<Reference> &ref_sketches,
+                     const std::vector<size_t> &kmer_lengths,
+                     RandomMC &random_chance, const bool jaccard,
+                     const int kNN, const size_t dist_col,
+                     const size_t num_threads);
+
 #ifdef GPU_AVAILABLE
 // defined in gpu_api.cpp
 std::vector<Reference> create_sketches_cuda(const std::string &db_name,
@@ -58,6 +64,13 @@ NumpyMatrix query_db_cuda(std::vector<Reference> &ref_sketches,
                           RandomMC &random_chance,
                           const int device_id = 0,
                           const unsigned int num_cpu_threads = 1);
+
+sparse_coo query_db_sparse_cuda(std::vector<Reference> &ref_sketches,
+                          const std::vector<size_t> &kmer_lengths,
+                          RandomMC &random_match,
+                          const int kNN, const size_t dist_col,
+                          const int device_id,
+                          const unsigned int num_cpu_threads);
 #endif
 
 bool same_db_version(const std::string &db1_name,
diff --git a/src/dist/matrix.hpp b/src/dist/matrix.hpp
index cf98f8b7..e7935334 100644
--- a/src/dist/matrix.hpp
+++ b/src/dist/matrix.hpp
@@ -6,6 +6,8 @@
  */
 #pragma once
 
+#include <algorithm>
+#include <numeric>
 #include <vector>
 #include <cstdint>
 #include <cstddef>
@@ -13,11 +15,26 @@
 
 #include <Eigen/Dense>
 
+#include "matrix_types.hpp"
 #include "matrix_idx.hpp"
 
-typedef Eigen::Matrix<float, Eigen::Dynamic, Eigen::Dynamic, Eigen::RowMajor> NumpyMatrix;
-typedef std::tuple<std::vector<long>, std::vector<long>, std::vector<float>> sparse_coo;
-typedef std::tuple<std::vector<long>, std::vector<long>, std::vector<long>> network_coo;
+// This type not used in any nvcc code
+using NumpyMatrix = Eigen::Matrix<float, Eigen::Dynamic, Eigen::Dynamic, Eigen::RowMajor>;
+
+//https://stackoverflow.com/a/12399290
+template <typename T>
+std::vector<long> sort_indexes(const T &v)
+{
+
+  // initialize original index locations
+  std::vector<long> idx(v.size());
+  std::iota(idx.begin(), idx.end(), 0);
+
+  std::stable_sort(idx.begin(), idx.end(),
+                   [&v](long i1, long i2) { return v[i1] < v[i2]; });
+
+  return idx;
+}
 
 NumpyMatrix long_to_square(const Eigen::VectorXf &rrDists,
                            const Eigen::VectorXf &qrDists,
diff --git a/src/dist/matrix_ops.cpp b/src/dist/matrix_ops.cpp
index 55b093dd..de48990e 100644
--- a/src/dist/matrix_ops.cpp
+++ b/src/dist/matrix_ops.cpp
@@ -4,59 +4,32 @@
  * Distance matrix transformations
  *
  */
-#include <vector>
-#include <numeric>
-#include <algorithm>
-#include <cstdint>
-#include <cstddef>
 #include <cmath>
-#include <string>
+#include <cstddef>
+#include <cstdint>
 #include <omp.h>
+#include <string>
+#include <vector>
 
 #include "api.hpp"
 
 const float epsilon = 1E-10;
 
 // Prototypes for cppying functions run in threads
-void square_block(const Eigen::VectorXf &longDists,
-                  NumpyMatrix &squareMatrix,
-                  const size_t n_samples,
-                  const size_t start,
-                  const size_t offset,
-                  const size_t max_elems);
+void square_block(const Eigen::VectorXf &longDists, NumpyMatrix &squareMatrix,
+                  const size_t n_samples, const size_t start,
+                  const size_t offset, const size_t max_elems);
 
 void rectangle_block(const Eigen::VectorXf &longDists,
-                     NumpyMatrix &squareMatrix,
-                     const size_t nrrSamples,
-                     const size_t nqqSamples,
-                     const size_t start,
+                     NumpyMatrix &squareMatrix, const size_t nrrSamples,
+                     const size_t nqqSamples, const size_t start,
                      const size_t max_elems);
 
-//https://stackoverflow.com/a/12399290
-template <typename T>
-std::vector<long> sort_indexes(const T &v)
-{
-
-  // initialize original index locations
-  std::vector<long> idx(v.size());
-  std::iota(idx.begin(), idx.end(), 0);
-
-  std::stable_sort(idx.begin(), idx.end(),
-                   [&v](long i1, long i2) { return v[i1] < v[i2]; });
-
-  return idx;
-}
-
-sparse_coo sparsify_dists(const NumpyMatrix &denseDists,
-                          const float distCutoff,
-                          const unsigned long int kNN)
-{
-  if (kNN > 0 && distCutoff > 0)
-  {
+sparse_coo sparsify_dists(const NumpyMatrix &denseDists, const float distCutoff,
+                          const unsigned long int kNN) {
+  if (kNN > 0 && distCutoff > 0) {
     throw std::runtime_error("Specify only one of kNN or distCutoff");
-  }
-  else if (kNN < 1 && distCutoff < 0)
-  {
+  } else if (kNN < 1 && distCutoff < 0) {
     throw std::runtime_error("kNN must be > 1 or distCutoff > 0");
   }
 
@@ -64,49 +37,36 @@ sparse_coo sparsify_dists(const NumpyMatrix &denseDists,
   std::vector<float> dists;
   std::vector<long> i_vec;
   std::vector<long> j_vec;
-  if (distCutoff > 0)
-  {
-    for (long i = 0; i < denseDists.rows(); i++)
-    {
-      for (long j = i + 1; j < denseDists.cols(); j++)
-      {
-        if (denseDists(i, j) < distCutoff)
-        {
+  if (distCutoff > 0) {
+    for (long i = 0; i < denseDists.rows(); i++) {
+      for (long j = i + 1; j < denseDists.cols(); j++) {
+        if (denseDists(i, j) < distCutoff) {
           dists.push_back(denseDists(i, j));
           i_vec.push_back(i);
           j_vec.push_back(j);
         }
       }
     }
-  }
-  else if (kNN >= 1)
-  {
+  } else if (kNN >= 1) {
     // Only add the k nearest (unique) neighbours
     // May be >k if repeats, often zeros
-    for (long i = 0; i < denseDists.rows(); i++)
-    {
+    for (long i = 0; i < denseDists.rows(); i++) {
       unsigned long unique_neighbors = 0;
       float prev_value = -1;
-      for (auto j : sort_indexes(denseDists.row(i)))
-      {
-        if (j == i)
-        {
+      for (auto j : sort_indexes(denseDists.row(i))) {
+        if (j == i) {
           continue; // Ignore diagonal which will always be one of the closest
         }
         bool new_val = abs(denseDists(i, j) - prev_value) < epsilon;
-        if (unique_neighbors < kNN || new_val)
-        {
+        if (unique_neighbors < kNN || new_val) {
           dists.push_back(denseDists(i, j));
           i_vec.push_back(i);
           j_vec.push_back(j);
-          if (!new_val)
-          {
+          if (!new_val) {
             unique_neighbors++;
             prev_value = denseDists(i, j);
           }
-        }
-        else
-        {
+        } else {
           break;
         }
       }
@@ -119,60 +79,52 @@ sparse_coo sparsify_dists(const NumpyMatrix &denseDists,
 NumpyMatrix long_to_square(const Eigen::VectorXf &rrDists,
                            const Eigen::VectorXf &qrDists,
                            const Eigen::VectorXf &qqDists,
-                           unsigned int num_threads)
-{
+                           unsigned int num_threads) {
   // Set up square matrix to move values into
   size_t nrrSamples = rows_to_samples(rrDists);
   size_t nqqSamples = 0;
-  if (qrDists.size() > 0)
-  {
+  if (qrDists.size() > 0) {
     nqqSamples = rows_to_samples(qqDists);
     // If one query, qqDists = [0] or None, so may come back as zero
-    if (nqqSamples < 1 && qrDists.size() > 0)
-    {
+    if (nqqSamples < 1 && qrDists.size() > 0) {
       nqqSamples = 1;
     }
-    if (qrDists.size() != nrrSamples * nqqSamples)
-    {
-      throw std::runtime_error("Shape of reference, query and ref vs query matrices mismatches");
+    if (qrDists.size() != nrrSamples * nqqSamples) {
+      throw std::runtime_error(
+          "Shape of reference, query and ref vs query matrices mismatches");
     }
   }
 
   // Initialise matrix and set diagonal to zero
   NumpyMatrix squareDists(nrrSamples + nqqSamples, nrrSamples + nqqSamples);
-  for (size_t diag_idx = 0; diag_idx < nrrSamples + nqqSamples; diag_idx++)
-  {
+  for (size_t diag_idx = 0; diag_idx < nrrSamples + nqqSamples; diag_idx++) {
     squareDists(diag_idx, diag_idx) = 0;
   }
 
 // Loop over threads for ref v ref square
 #pragma omp parallel for schedule(static) num_threads(num_threads)
-  for (long distIdx = 0; distIdx < rrDists.rows(); distIdx++)
-  {
+  for (long distIdx = 0; distIdx < rrDists.rows(); distIdx++) {
     unsigned long i = calc_row_idx(distIdx, nrrSamples);
     unsigned long j = calc_col_idx(distIdx, i, nrrSamples);
     squareDists(i, j) = rrDists[distIdx];
     squareDists(j, i) = rrDists[distIdx];
   }
 
-  if (qqDists.size() > 0)
-  {
+  if (qqDists.size() > 0) {
 #pragma omp parallel for schedule(static) num_threads(num_threads)
-    for (long distIdx = 0; distIdx < qqDists.rows(); distIdx++)
-    {
+    for (long distIdx = 0; distIdx < qqDists.rows(); distIdx++) {
       unsigned long i = calc_row_idx(distIdx, nqqSamples) + nrrSamples;
-      unsigned long j = calc_col_idx(distIdx, i - nrrSamples, nqqSamples) + nrrSamples;
+      unsigned long j =
+          calc_col_idx(distIdx, i - nrrSamples, nqqSamples) + nrrSamples;
       squareDists(i, j) = qqDists[distIdx];
       squareDists(j, i) = qqDists[distIdx];
     }
   }
 
   // Query vs ref rectangles
-  if (qrDists.size() > 0)
-  {
+  if (qrDists.size() > 0) {
 #pragma omp parallel for schedule(static) num_threads(num_threads)
-    for (long distIdx = 0; distIdx < qrDists.rows(); distIdx++)
-    {
+    for (long distIdx = 0; distIdx < qrDists.rows(); distIdx++) {
       unsigned long i = distIdx % nrrSamples;
       unsigned long j = distIdx / nrrSamples + nrrSamples;
       squareDists(i, j) = qrDists[distIdx];
@@ -184,10 +136,8 @@ NumpyMatrix long_to_square(const Eigen::VectorXf &rrDists,
 }
 
 Eigen::VectorXf square_to_long(const NumpyMatrix &squareDists,
-                               const unsigned int num_threads)
-{
-  if (squareDists.rows() != squareDists.cols())
-  {
+                               const unsigned int num_threads) {
+  if (squareDists.rows() != squareDists.cols()) {
     throw std::runtime_error("square_to_long input must be a square matrix");
   }
 
@@ -198,10 +148,8 @@ Eigen::VectorXf square_to_long(const NumpyMatrix &squareDists,
 // due to reverse direction
 // guided schedules inversely proportional to outer index
 #pragma omp parallel for schedule(guided, 1) num_threads(num_threads)
-  for (long i = n - 2; i >= 0; i--)
-  {
-    for (long j = i + 1; j < n; j++)
-    {
+  for (long i = n - 2; i >= 0; i--) {
+    for (long j = i + 1; j < n; j++) {
       longDists(square_to_condensed(i, j, n)) = squareDists(i, j);
     }
   }
diff --git a/src/dist/matrix_types.hpp b/src/dist/matrix_types.hpp
new file mode 100644
index 00000000..c7599b6f
--- /dev/null
+++ b/src/dist/matrix_types.hpp
@@ -0,0 +1,10 @@
+#pragma once
+
+// This file can be used by gcc and nvcc (i.e. no Eigen)
+// Compound types used by many functions in api, bindings etc
+
+#include <vector>
+#include <tuple>
+
+using sparse_coo = std::tuple<std::vector<long>, std::vector<long>, std::vector<float>>;
+using network_coo = std::tuple<std::vector<long>, std::vector<long>, std::vector<long>>;
\ No newline at end of file
diff --git a/src/gpu/align.hpp b/src/gpu/align.hpp
new file mode 100644
index 00000000..0a17ca2c
--- /dev/null
+++ b/src/gpu/align.hpp
@@ -0,0 +1,13 @@
+#pragma once
+
+// Align structs
+// https://stackoverflow.com/a/12779757
+#if defined(__CUDACC__) // NVCC
+#define ALIGN(n) __align__(n)
+#elif defined(__GNUC__) // GCC
+#define ALIGN(n) __attribute__((aligned(n)))
+#elif defined(_MSC_VER) // MSVC
+#define ALIGN(n) __declspec(align(n))
+#else
+#error "Please provide a definition for MY_ALIGN macro for your host compiler!"
+#endif
diff --git a/src/gpu/containers.cuh b/src/gpu/containers.cuh
new file mode 100644
index 00000000..4180b1fd
--- /dev/null
+++ b/src/gpu/containers.cuh
@@ -0,0 +1,176 @@
+#pragma once
+
+#include <vector>
+
+#include "cuda.cuh"
+
+template <typename T> class device_array {
+public:
+  // Default constructor
+  device_array() : data_(nullptr), size_(0) {}
+
+  // Constructor to allocate empty memory
+  device_array(const size_t size) : size_(size) {
+    CUDA_CALL(cudaMalloc((void **)&data_, size_ * sizeof(T)));
+    CUDA_CALL(cudaMemset(data_, 0, size_ * sizeof(T)));
+  }
+
+  // Constructor from vector
+  device_array(const std::vector<T> &data) : size_(data.size()) {
+    CUDA_CALL(cudaMalloc((void **)&data_, size_ * sizeof(T)));
+    CUDA_CALL(
+        cudaMemcpy(data_, data.data(), size_ * sizeof(T), cudaMemcpyDefault));
+  }
+
+  // Copy
+  device_array(const device_array &other) : size_(other.size_) {
+    CUDA_CALL(cudaMalloc((void **)&data_, size_ * sizeof(T)));
+    CUDA_CALL(
+        cudaMemcpy(data_, other.data_, size_ * sizeof(T), cudaMemcpyDefault));
+  }
+
+  // Copy assign
+  device_array &operator=(const device_array &other) {
+    if (this != &other) {
+      size_ = other.size_;
+      CUDA_CALL(cudaFree(data_));
+      CUDA_CALL(cudaMalloc((void **)&data_, size_ * sizeof(T)));
+      CUDA_CALL(
+          cudaMemcpy(data_, other.data_, size_ * sizeof(T), cudaMemcpyDefault));
+    }
+    return *this;
+  }
+
+  // Move
+  device_array(device_array &&other) : data_(nullptr), size_(0) {
+    data_ = other.data_;
+    size_ = other.size_;
+    other.data_ = nullptr;
+    other.size_ = 0;
+  }
+
+  // Move assign
+  device_array &operator=(device_array &&other) {
+    if (this != &other) {
+      CUDA_CALL(cudaFree(data_));
+      data_ = other.data_;
+      size_ = other.size_;
+      other.data_ = nullptr;
+      other.size_ = 0;
+    }
+    return *this;
+  }
+
+  ~device_array() { CUDA_CALL_NOTHROW(cudaFree(data_)); }
+
+  void swap(device_array& other) {
+    T* data_tmp = other.data_;
+    size_t size_tmp = other.size_;
+    other.data_ = data_;
+    other.size_ = size_;
+    data_ = data_tmp;
+    size_ = size_tmp;
+  }
+
+  void get_array(std::vector<T> &dst) const {
+    CUDA_CALL(cudaMemcpy(dst.data(), data_, dst.size() * sizeof(T),
+                         cudaMemcpyDefault));
+  }
+
+  void set_array(const std::vector<T> &src) {
+    size_ = src.size();
+    CUDA_CALL(
+        cudaMemcpy(data_, src.data(), size_ * sizeof(T), cudaMemcpyDefault));
+  }
+
+  void set_array(const T *src) {
+    CUDA_CALL(cudaMemcpy(data_, src, size_ * sizeof(T), cudaMemcpyDefault));
+  }
+
+  // Special functions for working with streams/graphs
+  // (prefer above functions unless using these)
+  void get_array_async(T *value, const size_t size, cudaStream_t stream) const {
+    if (size > size_) {
+      throw std::runtime_error("Size of host memory too big to copy");
+    }
+    CUDA_CALL(cudaMemcpyAsync((void *)value, data_, sizeof(T) * size,
+                              cudaMemcpyDefault, stream));
+  }
+
+  void set_array_async(const T *value, const size_t size, cudaStream_t stream) {
+    if (size > size_) {
+      throw std::runtime_error("Size of host memory too big to copy");
+    }
+    CUDA_CALL(cudaMemcpyAsync(data_, (void *)value, sizeof(T) * size,
+                              cudaMemcpyDefault, stream));
+  }
+
+  T *data() { return data_; }
+
+  size_t size() const { return size_; }
+
+private:
+  T *data_;
+  size_t size_;
+};
+
+// Specialisation of the above for void* memory needed by some cub functions
+// Construct once and use set_size() to modify
+template <> class device_array<void> {
+public:
+  // Default constructor
+  device_array() : data_(nullptr), size_(0) {}
+  // Constructor to allocate empty memory
+  device_array(const size_t size) : size_(size) {
+    if (size_ > 0) {
+      CUDA_CALL(cudaMalloc((void **)&data_, size_));
+    }
+  }
+
+  ~device_array() { CUDA_CALL_NOTHROW(cudaFree(data_)); }
+
+  void set_size(size_t size) {
+    if (size != size_) {
+      size_ = size;
+      CUDA_CALL(cudaFree(data_));
+      if (size_ > 0) {
+        CUDA_CALL(cudaMalloc((void **)&data_, size_));
+      } else {
+        data_ = nullptr;
+      }
+    }
+  }
+
+  void *data() { return data_; }
+
+  size_t size() const { return size_; }
+
+private:
+  device_array(const device_array<void> &) = delete;
+  device_array(device_array<void> &&) = delete;
+
+  void *data_;
+  size_t size_;
+};
+
+class cuda_stream {
+public:
+  cuda_stream() { CUDA_CALL(cudaStreamCreate(&stream_)); }
+
+  ~cuda_stream() {
+    if (stream_ != nullptr) {
+      CUDA_CALL_NOTHROW(cudaStreamDestroy(stream_));
+    }
+  }
+
+  cudaStream_t stream() { return stream_; }
+
+  void sync() { CUDA_CALL(cudaStreamSynchronize(stream_)); }
+
+private:
+  // Delete copy and move
+  cuda_stream(const cuda_stream &) = delete;
+  cuda_stream(cuda_stream &&) = delete;
+
+  cudaStream_t stream_;
+};
diff --git a/src/gpu/cuda.cuh b/src/gpu/cuda.cuh
index b9cb87af..57fe6da0 100644
--- a/src/gpu/cuda.cuh
+++ b/src/gpu/cuda.cuh
@@ -3,11 +3,16 @@
 #include <stdexcept>
 #include <stdio.h>
 
+#include <type_traits>
+static_assert(__CUDACC_VER_MAJOR__ >= 11, "CUDA >=11.0 required");
+
 #include <cuda.h>
 #include <cuda_runtime.h>
 #include <device_launch_parameters.h>
 
-const int progressBitshift = 10; // Update every 2^10 = 1024 dists
+#include "align.hpp"
+
+const int progress_blocks = 1000; // Update 1000
 
 static void HandleCUDAError(const char *file, int line,
                             cudaError_t status = cudaGetLastError()) {
@@ -28,32 +33,52 @@ static void HandleCUDAError(const char *file, int line,
 #define CUDA_CALL(err) (HandleCUDAError(__FILE__, __LINE__, err))
 #define CUDA_CALL_NOTHROW( err ) (err)
 
-// A bit lazy... should be a class and just use the destructor
-struct progress_atomics {
+struct ALIGN(16) progress_ptrs {
   volatile int *blocks_complete;
   bool *kill_kernel;
+};
 
-  void init() {
-    CUDA_CALL(cudaMallocManaged(&blocks_complete, sizeof(int)));
-    CUDA_CALL(cudaMallocManaged(&kill_kernel, sizeof(bool)));
-    *blocks_complete = 0;
-    *kill_kernel = false;
+class progress_atomics {
+public:
+  progress_atomics() {
+    CUDA_CALL(cudaMallocManaged((void**)&(managed_ptrs.blocks_complete), sizeof(int)));
+    CUDA_CALL(cudaMallocManaged((void**)&(managed_ptrs.kill_kernel), sizeof(bool)));
+    *(managed_ptrs.blocks_complete) = 0;
+    *(managed_ptrs.kill_kernel) = false;
   }
 
-  void free() {
-    CUDA_CALL(cudaFree((void *)blocks_complete));
-    CUDA_CALL(cudaFree(kill_kernel));
+  ~progress_atomics() {
+    CUDA_CALL(cudaFree((void *)managed_ptrs.blocks_complete));
+    CUDA_CALL(cudaFree(managed_ptrs.kill_kernel));
   }
+
+  void set_kill() {
+    *(managed_ptrs.kill_kernel) = true;
+  }
+
+  progress_ptrs get_ptrs() {
+    return managed_ptrs;
+  }
+
+  __host__
+  int complete() {
+    return *(managed_ptrs.blocks_complete);
+  }
+
+private:
+  progress_atomics(const progress_atomics &) = delete;
+  progress_atomics(progress_atomics &&) = delete;
+
+  progress_ptrs managed_ptrs;
 };
 
 // Use atomic add to update a counter, so progress works regardless of
 // dispatch order
 __device__ inline void update_progress(long long dist_idx, long long dist_n,
-                                       progress_atomics progress) {
+                                       progress_ptrs &progress) {
   // Progress indicator
-  // The >> progressBitshift is a divide by 1024 - update roughly every 0.1%
-  if (dist_idx % (dist_n >> progressBitshift) == 0) {
-    if (*(progress.kill_kernel) == true) {
+  if (dist_idx % (dist_n / progress_blocks) == 0) {
+    if (*(progress.kill_kernel)) {
       __trap();
     }
     atomicAdd((int *)progress.blocks_complete, 1);
diff --git a/src/gpu/device_memory.cuh b/src/gpu/device_memory.cuh
index 2795010d..563d1cfa 100644
--- a/src/gpu/device_memory.cuh
+++ b/src/gpu/device_memory.cuh
@@ -1,26 +1,15 @@
 #pragma once
 
+#include "align.hpp"
 #include "reference.hpp"
 
-// Align structs
-// https://stackoverflow.com/a/12779757
-#if defined(__CUDACC__) // NVCC
-#define ALIGN(n) __align__(n)
-#elif defined(__GNUC__) // GCC
-#define ALIGN(n) __attribute__((aligned(n)))
-#elif defined(_MSC_VER) // MSVC
-#define ALIGN(n) __declspec(align(n))
-#else
-#error "Please provide a definition for MY_ALIGN macro for your host compiler!"
-#endif
-
 struct ALIGN(8) RandomStrides {
   size_t kmer_stride;
   size_t cluster_inner_stride;
   size_t cluster_outer_stride;
 };
 
-typedef std::tuple<RandomStrides, std::vector<float>> FlatRandom;
+using FlatRandom = std::tuple<RandomStrides, std::vector<float>>;
 
 #ifdef GPU_AVAILABLE
 
diff --git a/src/gpu/dist.cu b/src/gpu/dist.cu
index eea5c21e..1f70683b 100644
--- a/src/gpu/dist.cu
+++ b/src/gpu/dist.cu
@@ -9,6 +9,7 @@
 // std
 #include <algorithm>
 #include <assert.h>
+#include <cfloat>
 #include <cmath>
 #include <cstdint>
 #include <iomanip>
@@ -23,14 +24,14 @@
 namespace py = pybind11;
 
 // memcpy_async
-#if __CUDACC_VER_MAJOR__ >= 11
+#include <cub/cub.cuh>
 #include <cuda/barrier>
 #include <cooperative_groups.h>
-#pragma diag_suppress static_var_with_dynamic_init
-#endif
+#pragma nv_diag_suppress static_var_with_dynamic_init
 
 // internal headers
 #include "cuda.cuh"
+#include "containers.cuh"
 #include "dist/matrix_idx.hpp"
 #include "gpu.hpp"
 #include "sketch/bitfuncs.hpp"
@@ -85,15 +86,15 @@ __device__ float jaccard_dist(const uint64_t *sketch1, const uint64_t *sketch2,
 // Simple linear regression, exact solution
 // Avoids use of dynamic memory allocation on device, or
 // linear algebra libraries
-__device__ void simple_linear_regression(float *core_dist,
-                                         float *accessory_dist,
+__device__ void simple_linear_regression(float dists[],
                                          const float xsum, const float ysum,
                                          const float xysum,
                                          const float xsquaresum,
-                                         const float ysquaresum, const int n) {
+                                         const float ysquaresum, const int n,
+                                         const int dist_col) {
   if (n < 2) {
-    *core_dist = 0;
-    *accessory_dist = 0;
+    dists[0] = 0.0f;
+    dists[1] = 0.0f;
   } else {
     // CUDA fast-math intrinsics on floats, which give comparable accuracy
     // Speed gain is fairly minimal, as most time spent on Jaccard distance
@@ -113,16 +114,16 @@ __device__ void simple_linear_regression(float *core_dist,
     // Store core/accessory in dists, truncating at zero
     // Memory should be initialised to zero so else block not strictly
     // necessary, but better safe than sorry!
-    if (beta < 0) {
-      *core_dist = 1 - __expf(beta);
+    if (beta < 0.0f) {
+      dists[0] = 1.0f - __expf(beta);
     } else {
-      *core_dist = 0;
+      dists[0] = 0.0f;
     }
 
-    if (alpha < 0) {
-      *accessory_dist = 1 - __expf(alpha);
+    if (alpha < 0.0f) {
+      dists[1] = 1.0f - __expf(alpha);
     } else {
-      *accessory_dist = 0;
+      dists[1] = 0.0f;
     }
   }
 }
@@ -136,6 +137,30 @@ __device__ void simple_linear_regression(float *core_dist,
 // Main kernel functions run on the device,
 // but callable from the host
 
+__global__ void set_idx(long* idx, size_t row_samples, size_t col_samples, size_t col_offset) {
+  for (int i = blockIdx.x * blockDim.x + threadIdx.x; i < row_samples * col_samples;
+    i += blockDim.x * gridDim.x) {
+    idx[i] = col_offset + i % col_samples;
+  }
+}
+
+__global__ void copy_top_k(float* sorted_dists, long* sorted_idx,
+  float* all_sorted_dists, long* all_sorted_idx, int segment_size, int n_out,
+    int kNN, int sketch_block_idx, int n_chunks, bool second_sort) {
+  for (int i = blockIdx.x * blockDim.x + threadIdx.x; i < n_out;
+    i += blockDim.x * gridDim.x) {
+    const int offset_in = (i / kNN) * segment_size + i % kNN;
+    int offset_out = i;
+    // If copying from the sorted kNN * n_chunk list into the final sparse matrix
+    if (!second_sort) {
+      // If copying from the sorted n_chunk dense list into the kNN * n_chunk list
+      offset_out += ((i / kNN) * (kNN * (n_chunks - 1))) + (kNN * sketch_block_idx);
+    }
+    all_sorted_dists[offset_out] = sorted_dists[offset_in];
+    all_sorted_idx[offset_out] = sorted_idx[offset_in];
+  }
+}
+
 __global__ void calculate_dists(
     const bool self, const uint64_t *ref, const long ref_n,
     const uint64_t *query, const long query_n, const int *kmers,
@@ -143,7 +168,8 @@ __global__ void calculate_dists(
     const float *random_table, const uint16_t *ref_idx_lookup,
     const uint16_t *query_idx_lookup, const SketchStrides ref_strides,
     const SketchStrides query_strides, const RandomStrides random_strides,
-    progress_atomics progress, const bool use_shared) {
+    progress_ptrs progress, const bool use_shared,
+    const int dist_col, const bool max_diagonal) {
   // Calculate indices for query, ref and results
   int ref_idx, query_idx, dist_idx;
   if (self) {
@@ -181,11 +207,11 @@ __global__ void calculate_dists(
 
   // Calculate Jaccard distances over k-mer lengths
   int kmer_used = 0;
-  float xsum = 0;
-  float ysum = 0;
-  float xysum = 0;
-  float xsquaresum = 0;
-  float ysquaresum = 0;
+  float xsum = 0.0f;
+  float ysum = 0.0f;
+  float xysum = 0.0f;
+  float xsquaresum = 0.0f;
+  float ysquaresum = 0.0f;
   bool stop = false;
   for (int kmer_idx = 0; kmer_idx < kmer_n; kmer_idx++) {
     // Copy query sketch into __shared__ mem
@@ -199,27 +225,21 @@ __global__ void calculate_dists(
     const uint64_t *query_ptr;
     extern __shared__ uint64_t query_shared[];
     int query_bin_strides;
-#if __CUDACC_VER_MAJOR__ >= 11
     auto block = cooperative_groups::this_thread_block();
     __shared__ cuda::barrier<cuda::thread_scope::thread_scope_block> barrier;
     if (block.thread_rank() == 0) {
       init(&barrier, block.size()); // Friend function initializes barrier
     }
     block.sync();
-#endif
     if (use_shared) {
       size_t sketch_bins = query_strides.bbits * query_strides.sketchsize64;
       size_t sketch_stride = query_strides.bin_stride;
       if (threadIdx.x < warp_size) {
         for (int lidx = threadIdx.x; lidx < sketch_bins; lidx += warp_size) {
-#if __CUDACC_VER_MAJOR__ >= 11
           cuda::memcpy_async(query_shared + lidx,
                              query_start + (lidx * sketch_stride),
                              sizeof(uint64_t),
                              barrier);
-#else
-          query_shared[lidx] = query_start[lidx * sketch_stride];
-#endif
         }
       }
       query_ptr = query_shared;
@@ -228,11 +248,7 @@ __global__ void calculate_dists(
       query_ptr = query_start;
       query_bin_strides = query_strides.bin_stride;
     }
-#if __CUDACC_VER_MAJOR__ >= 11
     barrier.arrive_and_wait();
-#else
-    __syncthreads();
-#endif
 
     // Some threads at the end of the last block will have nothing to do
     // Need to have conditional here to avoid block on __syncthreads() above
@@ -280,8 +296,21 @@ __global__ void calculate_dists(
 
   if (ref_idx < ref_n) {
     // Run the regression, and store results in dists
-    simple_linear_regression(dists + dist_idx, dists + dist_n + dist_idx, xsum,
-                             ysum, xysum, xsquaresum, ysquaresum, kmer_used);
+    float fitted_dists[2];
+    // Set diagonal if you wish to ignore diagonals (set them to max)
+    if (max_diagonal && ref_idx == query_idx) {
+      fitted_dists[0] = INFINITY;
+      fitted_dists[1] = INFINITY;
+    } else {
+      simple_linear_regression(fitted_dists, xsum, ysum, xysum, xsquaresum,
+                               ysquaresum, kmer_used, dist_col);
+    }
+    if (dist_col < 0) {
+      dists[dist_idx] = fitted_dists[0];
+      dists[dist_idx + dist_n] = fitted_dists[1];
+    } else {
+      dists[dist_idx] = fitted_dists[dist_col];
+    }
 
     update_progress(dist_idx, dist_n, progress);
   }
@@ -304,7 +333,7 @@ std::tuple<size_t, size_t> getBlockSize(const size_t ref_samples,
   // We take the next multiple of 32 that is larger than the number of
   // reference sketches, up to a maximum of 512
   size_t blockSize =
-      std::min(512, 32 * static_cast<int>((ref_samples + 32 - 1) / 32));
+      std::min(256, 32 * static_cast<int>((ref_samples + 32 - 1) / 32));
   size_t blockCount = 0;
   if (self) {
     for (int i = 0; i < ref_samples; i++) {
@@ -319,18 +348,18 @@ std::tuple<size_t, size_t> getBlockSize(const size_t ref_samples,
 
 // Writes a progress meter using the device int which keeps
 // track of completed jobs
-void reportDistProgress(progress_atomics progress, long long dist_rows) {
-  long long progress_blocks = 1 << progressBitshift;
+void reportDistProgress(progress_atomics& progress, long long dist_rows) {
   int now_completed = 0;
   float kern_progress = 0;
   if (dist_rows > progress_blocks) {
     while (now_completed < progress_blocks - 1) {
       if (PyErr_CheckSignals() != 0) {
-        *(progress.kill_kernel) = true;
+        progress.set_kill();
         throw py::error_already_set();
       }
-      if (*(progress.blocks_complete) > now_completed) {
-        now_completed = *(progress.blocks_complete);
+      int complete = progress.complete();
+      if (complete > now_completed) {
+        now_completed = complete;
         kern_progress = now_completed / (float)progress_blocks;
         fprintf(stderr, "%cProgress (GPU): %.1lf%%", 13, kern_progress * 100);
       } else {
@@ -354,6 +383,24 @@ std::tuple<size_t, size_t, size_t> initialise_device(const int device_id) {
       std::make_tuple(mem_free, mem_total, static_cast<size_t>(shared_size)));
 }
 
+std::tuple<bool, size_t> check_shared_size(const SketchStrides& strides, const size_t shared_size) {
+  size_t sketch_size_bytes =
+    strides.sketchsize64 * strides.bbits * sizeof(uint64_t);
+  bool use_shared = true;
+  if (sketch_size_bytes > shared_size) {
+    std::cerr << "You are using a large sketch size, which may slow down "
+                "computation on this device"
+              << std::endl;
+    std::cerr << "Reduce sketch size to "
+              << std::floor(64 * shared_size /
+                            (strides.bbits * sizeof(uint64_t)))
+              << " or less for better performance" << std::endl;
+    sketch_size_bytes = 0;
+    use_shared = false;
+  }
+  return std::make_tuple(use_shared, sketch_size_bytes);
+}
+
 // Main function to run the distance calculations, reading/writing into
 // device_arrays Cache preferences: Upper dist memory access is hard to predict,
 // so try and cache as much as possible Query uses on-chip cache (__shared__) to
@@ -374,7 +421,6 @@ std::vector<float> dispatchDists(std::vector<Reference> &ref_sketches,
 
   // Progress meter
   progress_atomics progress;
-  progress.init();
 
   RandomStrides random_strides = std::get<0>(flat_random);
   long long dist_rows;
@@ -391,22 +437,14 @@ std::vector<float> dispatchDists(std::vector<Reference> &ref_sketches,
                              ref_random_idx, query_random_idx, kmer_lengths,
                              dist_rows, self, cpu_threads);
 
-  size_t sketch_size_bytes =
-      query_strides.sketchsize64 * query_strides.bbits * sizeof(uint64_t);
-  bool use_shared = true;
-  if (sketch_size_bytes > shared_size) {
-    std::cerr << "You are using a large sketch size, which may slow down "
-                 "computation on this device"
-              << std::endl;
-    std::cerr << "Reduce sketch size to "
-              << std::floor(64 * shared_size /
-                            (query_strides.bbits * sizeof(uint64_t)))
-              << " or less for better performance" << std::endl;
-    sketch_size_bytes = 0;
-    use_shared = false;
-  }
+  bool use_shared;
+  size_t shared_size_bytes;
+  std::tie(use_shared, shared_size_bytes) =
+    check_shared_size(query_strides, shared_size);
 
   size_t blockSize, blockCount;
+  bool max_diagonal = false;
+  int dist_col = -1;
   if (self) {
     std::tie(blockSize, blockCount) = getBlockSize(
         sketch_subsample.ref_size, sketch_subsample.ref_size, dist_rows, self);
@@ -414,13 +452,13 @@ std::vector<float> dispatchDists(std::vector<Reference> &ref_sketches,
     // Third argument is the size of __shared__ memory needed by a thread block
     // This is equal to the query sketch size in bytes (at a single k-mer
     // length)
-    calculate_dists<<<blockCount, blockSize, sketch_size_bytes>>>(
+    calculate_dists<<<blockCount, blockSize, shared_size_bytes>>>(
         self, device_arrays.ref_sketches(), sketch_subsample.ref_size,
         device_arrays.ref_sketches(), sketch_subsample.ref_size,
         device_arrays.kmers(), kmer_lengths.size(), device_arrays.dist_mat(),
         dist_rows, device_arrays.random_table(), device_arrays.ref_random(),
         device_arrays.ref_random(), ref_strides, ref_strides, random_strides,
-        progress, use_shared);
+        progress.get_ptrs(), use_shared, dist_col, max_diagonal);
   } else {
     std::tie(blockSize, blockCount) =
         getBlockSize(sketch_subsample.ref_size, sketch_subsample.query_size,
@@ -429,20 +467,19 @@ std::vector<float> dispatchDists(std::vector<Reference> &ref_sketches,
     // Third argument is the size of __shared__ memory needed by a thread block
     // This is equal to the query sketch size in bytes (at a single k-mer
     // length)
-    calculate_dists<<<blockCount, blockSize, sketch_size_bytes>>>(
+    calculate_dists<<<blockCount, blockSize, shared_size_bytes>>>(
         self, device_arrays.ref_sketches(), sketch_subsample.ref_size,
         device_arrays.query_sketches(), sketch_subsample.query_size,
         device_arrays.kmers(), kmer_lengths.size(), device_arrays.dist_mat(),
         dist_rows, device_arrays.random_table(), device_arrays.ref_random(),
         device_arrays.query_random(), ref_strides, query_strides,
-        random_strides, progress, use_shared);
+        random_strides, progress.get_ptrs(), use_shared, dist_col, max_diagonal);
   }
 
   // Check for error in kernel launch
   CUDA_CALL(cudaGetLastError());
   reportDistProgress(progress, dist_rows);
   fprintf(stderr, "%cProgress (GPU): 100.0%%\n", 13);
-  progress.free();
 
   // Copy results back to host
   CUDA_CALL(cudaDeviceSynchronize());
@@ -450,3 +487,305 @@ std::vector<float> dispatchDists(std::vector<Reference> &ref_sketches,
 
   return (dist_results);
 }
+
+
+// Function which sparsifies distances on the fly. Distances are calculated in
+// blocks, sorted and top k stored.
+// NB graph probably not needed as API calls faster than ops here
+sparse_coo sparseDists(const dist_params params,
+  const std::vector<std::vector<uint64_t>> &ref_sketches,
+  const std::vector<SketchStrides> &ref_strides,
+  const FlatRandom &flat_random,
+  const std::vector<uint16_t> &ref_random_idx,
+  const std::vector<size_t> &kmer_lengths,
+  const int kNN,
+  const size_t dist_col,
+  const size_t samples_per_chunk,
+  const size_t num_big_chunks,
+  const int cpu_threads) {
+  /*
+   *
+   *   Device setup
+   *
+   */
+  CUDA_CALL(cudaDeviceSetCacheConfig(cudaFuncCachePreferL1));
+  CUDA_CALL(cudaDeviceSetSharedMemConfig(cudaSharedMemBankSizeEightByte));
+
+  // Progress meter (not used for printing in this function)
+  progress_atomics progress;
+
+  // const parameters for functions
+  const size_t n_chunks = ref_sketches.size();
+  const bool self = false;
+  const double total_blocks = static_cast<double>(n_chunks) * n_chunks;
+  const size_t idx_blockSize = 64;
+  const size_t copy_blockSize = 64;
+  const int begin_sort_bit = 0;
+  const int end_sort_bit = 8 * sizeof(float);
+
+  bool use_shared;
+  size_t shared_size_bytes;
+  std::tie(use_shared, shared_size_bytes) =
+    check_shared_size(ref_strides[0], params.shared_size);
+
+  /*
+   *
+   *   Memory allocations
+   *   All use the big_chunk size, for smaller chunks the end of the alloc
+   *   is not used
+   *
+   */
+
+  // Storage for results on host
+  std::vector<float> host_dists(params.n_samples * kNN);
+  std::vector<long> i_vec(params.n_samples * kNN);
+  std::vector<long> j_vec(params.n_samples * kNN);
+
+  //   sketch block 1
+  //   sketch block 2
+  //   sketch block 3
+  //   sketch block 4
+  device_array<uint64_t> block1(ref_sketches[0].size());
+  device_array<uint64_t> block2(block1.size());
+  device_array<uint64_t> block3(block1.size());
+  device_array<uint64_t> block4(block1.size());
+  //   dists (chunk size^2)
+  //   dists idx (dists.size())
+  device_array<float> dists((samples_per_chunk + 1) * (samples_per_chunk + 1));
+  device_array<long> dists_idx(dists.size());
+
+  // inner loop sorting
+  //   sorted dists (dists.size())
+  //   sorted dists idx (dists.size())
+  //   offsets
+  //   tmp space
+  device_array<float> sorted_dists(dists.size());
+  device_array<long> sorted_dists_idx(dists.size());
+  device_array<int> dist_partitions((samples_per_chunk + 1) + 1);
+  device_array<void> dist_sort_tmp;
+
+  // inner loop kNN pick (copy_top_k)
+  //   sorted dists
+  //   sorted dists idx
+  device_array<float> all_sorted_dists(kNN * n_chunks * (samples_per_chunk + 1));
+  device_array<long> all_sorted_dists_idx(all_sorted_dists.size());
+
+  // outer loop sorting
+  //   doubly sorted dists (staging for final results, copied to host)
+  //   doubly sorted dists idx (as above)
+  //   offsets
+  //   tmp space
+  device_array<float> doubly_sorted_dists(all_sorted_dists.size());
+  device_array<long> doubly_sorted_dists_idx(all_sorted_dists.size());
+  std::vector<int> host_partitions;
+  for (int partition_idx = 0; partition_idx < dist_partitions.size(); ++partition_idx) {
+    host_partitions.push_back(partition_idx * kNN * n_chunks);
+  }
+  device_array<int> second_sort_partitions(host_partitions);
+  host_partitions.clear();
+  device_array<void> outer_dist_sort_tmp;
+
+  // out loop kNN pick (copy_top_k)
+  //   final dists (staging for final results, copied to host)
+  //   final dists idx (as above)
+  device_array<float> final_dists(kNN * (samples_per_chunk + 1));
+  device_array<long> final_dists_idx(kNN * (samples_per_chunk + 1));
+
+  //   kmers
+  const std::vector<int> kmer_ints(kmer_lengths.begin(), kmer_lengths.end());
+  device_array<int> kmers(kmer_ints);
+
+  //   random
+  RandomStrides random_strides = std::get<0>(flat_random);
+  device_array<float> random_table(std::get<1>(flat_random));
+  device_array<uint16_t> random_idx(ref_random_idx);
+
+  /*
+   *
+   *   Memory setup
+   *   Set i_vec on host
+   *   Register sketches in host memory
+   *   Copy first sketch blocks to device
+   *
+   */
+
+  // Set i_vec (it just needs to be 0, 0, 0..., 1, 1, 1...)
+  #pragma omp parallel for num_threads(cpu_threads)
+  for (size_t sample_idx = 0; sample_idx < params.n_samples; ++sample_idx) {
+    std::fill_n(i_vec.begin() + sample_idx * kNN, kNN, sample_idx);
+  }
+
+  // Register sketches on host so they can be copied async
+  for (size_t chunk_idx = 0; chunk_idx < n_chunks; ++chunk_idx) {
+    CUDA_CALL(cudaHostRegister((void *)ref_sketches[chunk_idx].data(),
+                               ref_sketches[chunk_idx].size() * sizeof(uint64_t),
+                               cudaHostRegisterReadOnly));
+  }
+
+  // Four CUDA streams used in loops
+  cuda_stream dist_stream, idx_stream, mem_stream, sort_stream;
+
+  // Copy first set of refs in. Block 4 is used to store this permanently to stop
+  // two copies being needed when entering a new row
+  block1.set_array_async(ref_sketches[0].data(), ref_sketches[0].size(), mem_stream.stream());
+  block4.set_array_async(block1.data(), block1.size(), mem_stream.stream());
+
+  /*
+   *
+   *   Loop over chunks
+   *   Outer loop over chunks of rows
+   *   Inner loop over chunks of columns
+   *
+   */
+  fprintf(stderr, "%cProgress (GPU): %.1lf%%", 13, 0.0f);
+
+  // OUTER LOOP over n_chunks lots of refs
+  size_t row_offset = 0;
+  for (size_t row_chunk_idx = 0; row_chunk_idx < n_chunks; ++row_chunk_idx) {
+    size_t row_samples = samples_per_chunk + (row_chunk_idx < num_big_chunks ? 1 : 0);
+    size_t col_offset = 0;
+
+    //  INNER LOOP over n_chunks lots of queries
+    for (size_t col_chunk_idx = 0; col_chunk_idx < n_chunks; ++col_chunk_idx) {
+      size_t col_samples = samples_per_chunk + (col_chunk_idx < num_big_chunks ? 1 : 0);
+      size_t dist_rows = row_samples * col_samples;
+
+      // Check for interrupts
+      if (PyErr_CheckSignals() != 0) {
+        throw py::error_already_set();
+      }
+
+      // Only need to set new sort partitions if moving to a smaller chunk
+      if (host_partitions.size() != col_samples + 1) {
+        host_partitions.clear();
+        for (int partition_idx = 0; partition_idx < col_samples + 1; ++partition_idx) {
+          host_partitions.push_back(partition_idx * col_samples);
+        }
+        dist_partitions.set_array_async(host_partitions.data(), host_partitions.size(), sort_stream.stream());
+      }
+
+      //    (stream 1 async) Run dists on 1 vs 2
+      size_t blockSize, blockCount;
+      std::tie(blockSize, blockCount) =
+        getBlockSize(row_samples, col_samples, dist_rows, self);
+      uint64_t* query_ptr = col_chunk_idx == 0 ? block4.data() : block2.data();
+      bool max_diagonal = col_chunk_idx == row_chunk_idx;
+      mem_stream.sync();
+      // NB in calculate_dists ref idx changes fastest (so should be the column)
+      // so ref and query are 'backwards'
+      calculate_dists<<<blockCount, blockSize, shared_size_bytes, dist_stream.stream()>>>(
+        self,
+        query_ptr, col_samples,
+        block1.data(), row_samples,
+        kmers.data(), kmers.size(),
+        dists.data(), dist_rows,
+        random_table.data(), random_idx.data() + col_offset, random_idx.data() + row_offset,
+        ref_strides[col_chunk_idx], ref_strides[row_chunk_idx],
+        random_strides, progress.get_ptrs(), use_shared, dist_col, max_diagonal
+      );
+
+      //    (stream 2 async) Load next into block 3
+      //    swap ptrs for block 2 <-> 3
+      if (col_chunk_idx + 1 < n_chunks) {
+        block3.set_array_async(ref_sketches[col_chunk_idx + 1].data(), ref_sketches[col_chunk_idx + 1].size(), mem_stream.stream());
+        block2.swap(block3);
+      } else if (row_chunk_idx + 1 < n_chunks) {
+        block3.set_array_async(ref_sketches[row_chunk_idx + 1].data(), ref_sketches[row_chunk_idx + 1].size(), mem_stream.stream());
+        block1.swap(block3);
+      }
+
+      //    (stream 3 async) Set dist idx via kernel
+      const size_t idx_blockCount = (dist_rows + idx_blockSize - 1) / idx_blockSize;
+      set_idx<<<idx_blockCount, idx_blockSize, 0, idx_stream.stream()>>>(
+        dists_idx.data(), row_samples, col_samples, col_offset
+      );
+
+      //    (stream 4) cub::DeviceSegmentedRadixSort::SortPairs on dists, dists idx -> sorted dists, sorted dists idx
+      const int num_items = dist_rows;
+      const int num_segments = host_partitions.size() - 1;
+      int *d_offsets = dist_partitions.data();
+      float *d_keys_in = dists.data();
+      float *d_keys_out = sorted_dists.data();
+      long *d_values_in = dists_idx.data();
+      long *d_values_out = sorted_dists_idx.data();
+      // Determine temporary device storage requirements (first run only)
+      if (row_chunk_idx == 0 && col_chunk_idx == 0) {
+        size_t temp_storage_bytes = 0;
+        cub::DeviceSegmentedRadixSort::SortPairs(dist_sort_tmp.data(), temp_storage_bytes,
+            d_keys_in, d_keys_out, d_values_in, d_values_out,
+            num_items, num_segments, d_offsets, d_offsets + 1,
+            begin_sort_bit, end_sort_bit, sort_stream.stream());
+        dist_sort_tmp.set_size(temp_storage_bytes);
+      }
+      // Run sorting operation
+      dist_stream.sync();
+      idx_stream.sync();
+      size_t temp_storage_bytes = dist_sort_tmp.size();
+      cub::DeviceSegmentedRadixSort::SortPairs(dist_sort_tmp.data(), temp_storage_bytes,
+          d_keys_in, d_keys_out, d_values_in, d_values_out,
+          num_items, num_segments, d_offsets, d_offsets + 1,
+          begin_sort_bit, end_sort_bit, sort_stream.stream());
+
+      //    (stream 4) D->D copy of top kNN dists to start of dists
+      const bool second_sort = false;
+      const size_t dist_out_size = kNN * num_segments;
+      const size_t copy_blockCount = (dist_out_size + copy_blockSize - 1) / copy_blockSize;
+      copy_top_k<<<copy_blockCount, copy_blockSize, 0, sort_stream.stream()>>>(
+        sorted_dists.data(), sorted_dists_idx.data(), all_sorted_dists.data(), all_sorted_dists_idx.data(),
+        col_samples, dist_out_size, kNN, col_chunk_idx, n_chunks, second_sort
+      );
+
+      // Update progress
+      col_offset += col_samples;
+      const size_t blocks_done = row_chunk_idx * n_chunks + col_chunk_idx + 1;
+      fprintf(stderr, "%cProgress (GPU): %.1lf%%", 13, 100 * blocks_done / total_blocks);
+    }
+
+    const int num_items = all_sorted_dists.size();
+    const int num_segments = row_samples;
+    int *d_offsets = second_sort_partitions.data();
+    float *d_keys_in = all_sorted_dists.data();
+    float *d_keys_out = doubly_sorted_dists.data();
+    long *d_values_in = all_sorted_dists_idx.data();
+    long *d_values_out = doubly_sorted_dists_idx.data();
+    // Determine temporary device storage requirements
+    if (row_chunk_idx == 0) {
+      size_t temp_storage_bytes = 0;
+      cub::DeviceSegmentedRadixSort::SortPairs(outer_dist_sort_tmp.data(), temp_storage_bytes,
+          d_keys_in, d_keys_out, d_values_in, d_values_out,
+          num_items, num_segments, d_offsets, d_offsets + 1,
+          begin_sort_bit, end_sort_bit, sort_stream.stream());
+      outer_dist_sort_tmp.set_size(temp_storage_bytes);
+    }
+    // Run sorting operation
+    size_t temp_storage_bytes = outer_dist_sort_tmp.size();
+    cub::DeviceSegmentedRadixSort::SortPairs(outer_dist_sort_tmp.data(), temp_storage_bytes,
+        d_keys_in, d_keys_out, d_values_in, d_values_out,
+        num_items, num_segments, d_offsets, d_offsets + 1,
+        begin_sort_bit, end_sort_bit, sort_stream.stream());
+
+    // take top kNN
+    const bool second_sort = true;
+    const int block_offset = 0;
+    const size_t dist_out_size = kNN * num_segments;
+    const size_t copy_blockCount = (dist_out_size + copy_blockSize - 1) / copy_blockSize;
+    copy_top_k<<<copy_blockCount, copy_blockSize, 0, sort_stream.stream()>>>(
+      doubly_sorted_dists.data(), doubly_sorted_dists_idx.data(), final_dists.data(), final_dists_idx.data(),
+      kNN * n_chunks, dist_out_size, kNN, block_offset, n_chunks, second_sort
+    );
+    // Copy chunk of results back to host
+    final_dists.get_array_async(host_dists.data() + row_offset * kNN, dist_out_size, sort_stream.stream());
+    final_dists_idx.get_array_async(j_vec.data() + row_offset * kNN, dist_out_size, sort_stream.stream());
+
+    row_offset += row_samples;
+  }
+  fprintf(stderr, "%cProgress (GPU): 100.0%%\n", 13);
+  CUDA_CALL(cudaDeviceSynchronize());
+
+  // Unregister host memory
+  for (size_t chunk_idx = 0; chunk_idx < n_chunks; ++chunk_idx) {
+    CUDA_CALL(cudaHostUnregister((void *)ref_sketches[chunk_idx].data()));
+  }
+
+  return (std::make_tuple(i_vec, j_vec, host_dists));
+}
diff --git a/src/gpu/gpu.hpp b/src/gpu/gpu.hpp
index 1604cb40..4661222b 100644
--- a/src/gpu/gpu.hpp
+++ b/src/gpu/gpu.hpp
@@ -14,10 +14,24 @@
 #include "device_memory.cuh"
 #include "gpu_countmin.cuh"
 #include "device_reads.cuh"
+#include "dist/matrix_types.hpp"
 
 static const int warp_size = 32;
 
 #ifdef GPU_AVAILABLE
+
+// Small struct used in cuda_dists_init
+struct ALIGN(16) dist_params {
+  bool self;
+  SketchStrides ref_strides;
+  SketchStrides query_strides;
+  long long dist_rows;
+  long n_samples;
+  size_t shared_size;
+  size_t mem_free;
+  size_t mem_total;
+};
+
 // defined in dist.cu
 std::tuple<size_t, size_t, size_t> initialise_device(const int device_id);
 
@@ -46,6 +60,18 @@ std::vector<float> dispatchDists(std::vector<Reference> &ref_sketches,
                                  const bool self, const int cpu_threads,
                                  const size_t shared_size);
 
+sparse_coo sparseDists(const dist_params params,
+  const std::vector<std::vector<uint64_t>> &ref_sketches,
+  const std::vector<SketchStrides> &ref_strides,
+  const FlatRandom &flat_random,
+  const std::vector<uint16_t> &ref_random_idx,
+  const std::vector<size_t> &kmer_lengths,
+  const int kNN,
+  const size_t dist_col,
+  const size_t samples_per_chunk,
+  const size_t num_big_chunks,
+  const int cpu_threads);
+
 // in sketch.cu
 void copyNtHashTablesToDevice();
 
diff --git a/src/gpu/gpu_api.cpp b/src/gpu/gpu_api.cpp
index 4329d5d3..8458c8eb 100644
--- a/src/gpu/gpu_api.cpp
+++ b/src/gpu/gpu_api.cpp
@@ -303,65 +303,90 @@ flatten_by_samples(const std::vector<Reference> &sketches,
   return flat_ref;
 }
 
-// Main function callable via API
-// Checks inputs
-// Flattens sketches
-// Copies flattened sketches to device
-// Runs kernel function across distance elements
-// Copies and returns results
-NumpyMatrix query_db_cuda(std::vector<Reference> &ref_sketches,
-                          std::vector<Reference> &query_sketches,
-                          const std::vector<size_t> &kmer_lengths,
-                          RandomMC &random_match, const int device_id,
-                          const unsigned int num_cpu_threads) {
-  size_t mem_free, mem_total, shared_size;
-  std::tie(mem_free, mem_total, shared_size) = initialise_device(device_id);
+
+// Helper function which checks device memory size and whether chunking
+// of input is necessary
+dist_params cuda_dists_init(const std::vector<Reference> &ref_sketches,
+                     const std::vector<Reference> &query_sketches,
+                     const std::vector<size_t> &kmer_lengths,
+                     const int device_id) {
+  dist_params params;
+
+  std::tie(params.mem_free, params.mem_total, params.shared_size) =
+    initialise_device(device_id);
   std::cerr << "Calculating distances on GPU device " << device_id << std::endl;
 
   // Check sketches are compatible
-  bool self = false;
+  params.self = false;
   size_t bbits = ref_sketches[0].bbits();
   size_t sketchsize64 = ref_sketches[0].sketchsize64();
   checkSketchParamsMatch(ref_sketches, kmer_lengths, bbits, sketchsize64);
 
   // Set up sketch information and sizes
-  SketchStrides ref_strides;
-  ref_strides.bbits = bbits;
-  ref_strides.sketchsize64 = sketchsize64;
-  SketchStrides query_strides = ref_strides;
+  params.ref_strides.bbits = bbits;
+  params.ref_strides.sketchsize64 = sketchsize64;
+  params.query_strides = params.ref_strides;
 
-  long long dist_rows;
-  long n_samples = 0;
   if (ref_sketches == query_sketches) {
-    self = true;
-    dist_rows = static_cast<long long>(0.5 * (ref_sketches.size()) *
+    params.self = true;
+    params.dist_rows = static_cast<long long>(0.5 * (ref_sketches.size()) *
                                        (ref_sketches.size() - 1));
-    n_samples = ref_sketches.size();
+    params.n_samples = ref_sketches.size();
   } else {
     // Also check query sketches are compatible
     checkSketchParamsMatch(query_sketches, kmer_lengths, bbits, sketchsize64);
-    dist_rows = ref_sketches.size() * query_sketches.size();
-    n_samples = ref_sketches.size() + query_sketches.size();
+    params.dist_rows = ref_sketches.size() * query_sketches.size();
+    params.n_samples = ref_sketches.size() + query_sketches.size();
   }
+
+  return params;
+}
+
+// Main function callable via API
+// Checks inputs
+// Flattens sketches
+// Copies flattened sketches to device
+// Runs kernel function across distance elements
+// Copies and returns results
+NumpyMatrix query_db_cuda(std::vector<Reference> &ref_sketches,
+                          std::vector<Reference> &query_sketches,
+                          const std::vector<size_t> &kmer_lengths,
+                          RandomMC &random_match, const int device_id,
+                          const unsigned int num_cpu_threads) {
+  dist_params params =
+    cuda_dists_init(ref_sketches, query_sketches, kmer_lengths, device_id);
+
   double est_size =
-      (bbits * sketchsize64 * kmer_lengths.size() * n_samples *
+      (params.ref_strides.bbits * params.ref_strides.sketchsize64 * kmer_lengths.size() * params.n_samples *
            sizeof(uint64_t) + // Size of sketches
        kmer_lengths.size() * std::pow(random_match.n_clusters(), 2) *
            sizeof(float) + // Size of random matches
-       n_samples * sizeof(uint16_t) +
-       dist_rows * 2 * sizeof(float)); // Size of distance matrix
+       params.n_samples * sizeof(uint16_t) +
+       params.dist_rows * 2 * sizeof(float)); // Size of distance matrix
   std::cerr << "Estimated device memory required: " << std::fixed
             << std::setprecision(0) << est_size / (1048576) << "Mb"
             << std::endl;
   std::cerr << "Total device memory: " << std::fixed << std::setprecision(0)
-            << mem_total / (1048576) << "Mb" << std::endl;
+            << params.mem_total / (1048576) << "Mb" << std::endl;
   std::cerr << "Free device memory: " << std::fixed << std::setprecision(0)
-            << mem_free / (1048576) << "Mb" << std::endl;
+            << params.mem_free / (1048576) << "Mb" << std::endl;
 
-  if (est_size > mem_free * (1 - mem_epsilon) && !self) {
-    throw std::runtime_error(
-        "Using greater than device memory is unsupported for query mode. "
-        "Split your input into smaller chunks");
+  unsigned int chunks = 1;
+  if (est_size > params.mem_free * (1 - mem_epsilon)) {
+    if (params.self) {
+      // To prevent memory being exceeded, total distance matrix is split up into
+      // chunks which do fit in memory. The most is needed in the 'corners' where
+      // two separate lots of sketches are loaded, hence the factor of two below
+
+      // These are iterated over in the same order as a square distance matrix.
+      // The i = j chunks are 'self', i < j can be skipped
+      // as they contain only lower triangle values, i > j work as query vs ref
+      chunks = floor((est_size * 2) / (params.mem_free * (1 - mem_epsilon))) + 1;
+    } else {
+      throw std::runtime_error(
+          "Using greater than device memory is unsupported for query mode. "
+          "Split your input into smaller chunks");
+    }
   }
 
   // Turn the random matches into an array (same for any ref, query or subsample
@@ -380,27 +405,15 @@ NumpyMatrix query_db_cuda(std::vector<Reference> &ref_sketches,
 
   // Ready to run dists on device
   SketchSlice sketch_subsample;
-  unsigned int chunks = 1;
-  std::vector<float> dist_results(dist_rows * 2);
+  std::vector<float> dist_results(params.dist_rows * 2);
   NumpyMatrix coreSquare, accessorySquare;
-  if (self) {
-    // To prevent memory being exceeded, total distance matrix is split up into
-    // chunks which do fit in memory. The most is needed in the 'corners' where
-    // two separate lots of sketches are loaded, hence the factor of two below
-
-    // These are iterated over in the same order as a square distance matrix.
-    // The i = j chunks are 'self', i < j can be skipped
-    // as they contain only lower triangle values, i > j work as query vs ref
-    if (est_size > mem_free * (1 - mem_epsilon)) {
-      chunks = floor((est_size * 2) / (mem_free * (1 - mem_epsilon))) + 1;
-    }
-    size_t calc_per_chunk = n_samples / chunks;
-    unsigned int num_big_chunks = n_samples % chunks;
-
+  if (params.self) {
+    size_t calc_per_chunk = params.n_samples / chunks;
+    unsigned int num_big_chunks = params.n_samples % chunks;
     // Only allocate these square matrices if they are needed
     if (chunks > 1) {
-      coreSquare.resize(n_samples, n_samples);
-      accessorySquare.resize(n_samples, n_samples);
+      coreSquare.resize(params.n_samples, params.n_samples);
+      accessorySquare.resize(params.n_samples, params.n_samples);
     }
     unsigned int total_chunks = (chunks * (chunks + 1)) >> 1;
     unsigned int chunk_count = 0;
@@ -425,9 +438,9 @@ NumpyMatrix query_db_cuda(std::vector<Reference> &ref_sketches,
         }
 
         dist_results = dispatchDists(
-            ref_sketches, ref_sketches, ref_strides, query_strides, flat_random,
+            ref_sketches, ref_sketches, params.ref_strides, params.query_strides, flat_random,
             ref_random_idx, ref_random_idx, sketch_subsample, kmer_lengths,
-            chunk_i == chunk_j, num_cpu_threads, shared_size);
+            chunk_i == chunk_j, num_cpu_threads, params.shared_size);
 
         // Read intermediate dists out
         if (chunks > 1) {
@@ -455,13 +468,13 @@ NumpyMatrix query_db_cuda(std::vector<Reference> &ref_sketches,
     sketch_subsample.query_offset = 0;
 
     dist_results = dispatchDists(
-        ref_sketches, query_sketches, ref_strides, query_strides, flat_random,
+        ref_sketches, query_sketches, params.ref_strides, params.query_strides, flat_random,
         ref_random_idx, query_random_idx, sketch_subsample, kmer_lengths, false,
-        num_cpu_threads, shared_size);
+        num_cpu_threads, params.shared_size);
   }
 
   NumpyMatrix dists_ret_matrix;
-  if (self && chunks > 1) {
+  if (params.self && chunks > 1) {
     // Chunked computation yields square matrix, which needs to be converted
     // back to long form
     Eigen::VectorXf core_dists = square_to_long(coreSquare, num_cpu_threads);
@@ -477,4 +490,41 @@ NumpyMatrix query_db_cuda(std::vector<Reference> &ref_sketches,
   }
 
   return dists_ret_matrix;
-}
\ No newline at end of file
+}
+
+sparse_coo query_db_sparse_cuda(std::vector<Reference> &ref_sketches,
+                          const std::vector<size_t> &kmer_lengths,
+                          RandomMC &random_match,
+                          const int kNN, const size_t dist_col,
+                          const int device_id,
+                          const unsigned int num_cpu_threads) {
+  dist_params params =
+    cuda_dists_init(ref_sketches, ref_sketches, kmer_lengths, device_id);
+  FlatRandom flat_random =
+      random_match.flattened_random(kmer_lengths, ref_sketches[0].seq_length());
+  std::vector<uint16_t> ref_random_idx =
+      random_match.lookup_array(ref_sketches);
+
+  // Flatten all of the sketches in blocks
+  //   (as stride is bins) - return vector<vector<uint64_t>>
+  // Use a fixed chunk size of up to 3000 samples per chunk
+  const size_t chunks = 1 + params.n_samples / 3000;
+  const size_t samples_per_chunk = params.n_samples / chunks;
+  const unsigned int num_big_chunks = params.n_samples % chunks;
+  std::vector<std::vector<uint64_t>> sample_blocks;
+  std::vector<SketchStrides> sample_block_strides(chunks, params.ref_strides);
+  size_t start_idx = 0;
+  for (size_t chunk_idx = 0; chunk_idx < chunks; ++chunk_idx) {
+    size_t end_idx = start_idx + samples_per_chunk +
+      (chunk_idx < num_big_chunks ? 1 : 0);
+    sample_blocks.push_back(
+      flatten_by_samples(ref_sketches, kmer_lengths,
+              sample_block_strides[chunk_idx], start_idx, end_idx, num_cpu_threads));
+    start_idx = end_idx;
+  }
+
+  return sparseDists(params, sample_blocks, sample_block_strides,
+                     flat_random, ref_random_idx, kmer_lengths,
+                     kNN, dist_col, samples_per_chunk, num_big_chunks,
+                     num_cpu_threads);
+}
diff --git a/src/gpu/sketch.cu b/src/gpu/sketch.cu
index ece14eb9..d14475cf 100644
--- a/src/gpu/sketch.cu
+++ b/src/gpu/sketch.cu
@@ -14,7 +14,7 @@ namespace py = pybind11;
 // memcpy_async
 #include <cooperative_groups.h>
 #include <cooperative_groups/memcpy_async.h>
-#pragma diag_suppress static_var_with_dynamic_init
+#pragma nv_diag_suppress static_var_with_dynamic_init
 
 #include "cuda.cuh"
 #include "gpu.hpp"
diff --git a/src/sketchlib_bindings.cpp b/src/sketchlib_bindings.cpp
index 29891c5b..d1edd162 100644
--- a/src/sketchlib_bindings.cpp
+++ b/src/sketchlib_bindings.cpp
@@ -137,28 +137,53 @@ NumpyMatrix queryDatabase(const std::string &ref_db_name,
 }
 
 sparse_coo
-sparseQuery(const std::string &ref_db_name, const std::string &query_db_name,
-            const std::vector<std::string> &ref_names,
-            const std::vector<std::string> &query_names,
+querySelfSparse(const std::string &ref_db_name, const std::vector<std::string> &ref_names,
             std::vector<size_t> kmer_lengths, const bool random_correct = true,
-            const float dist_cutoff = 0, const unsigned long int kNN = 0,
-            const bool core = true, const size_t num_threads = 1,
-            const bool use_gpu = false, const int device_id = 0) {
-  NumpyMatrix dists = queryDatabase(ref_db_name, query_db_name, ref_names,
-                                    query_names, kmer_lengths, random_correct,
+            const bool jaccard = false, const unsigned long int kNN = 0,
+            const float dist_cutoff = 0.0f, const size_t dist_col = 0,
+            const size_t num_threads = 1, const bool use_gpu = false,
+            const int device_id = 0) {
+  if (use_gpu && (jaccard || kmer_lengths.size() < 2 || dist_col > 1)) {
+    throw std::runtime_error(
+        "Extracting Jaccard distances not supported on GPU");
+  }
+  std::vector<Reference> ref_sketches =
+      load_sketches(ref_db_name, ref_names, kmer_lengths, false);
+
+  RandomMC random;
+  if (random_correct) {
+    random = get_random(ref_db_name, ref_sketches[0].rc());
+  } else {
+    random = RandomMC();
+  }
+
+  sparse_coo sparse_dists;
+  if (dist_cutoff > 0) {
+    NumpyMatrix dists = queryDatabase(ref_db_name, ref_db_name, ref_names,
+                                    ref_names, kmer_lengths, random_correct,
                                     false, num_threads, use_gpu, device_id);
 
-  unsigned int dist_col = 0;
-  if (!core) {
-    dist_col = 1;
+    Eigen::VectorXf dummy_query_ref;
+    Eigen::VectorXf dummy_query_query;
+    NumpyMatrix long_form = long_to_square(dists.col(dist_col), dummy_query_ref,
+                                          dummy_query_query, num_threads);
+    sparse_dists = sparsify_dists(long_form, dist_cutoff, kNN);
+  } else {
+#ifdef GPU_AVAILABLE
+    if (use_gpu) {
+      sparse_dists = query_db_sparse_cuda(ref_sketches, kmer_lengths, random,
+        kNN, dist_col, device_id, num_threads);
+    } else {
+      sparse_dists = query_db_sparse(ref_sketches, kmer_lengths, random,
+        jaccard, kNN, dist_col, num_threads);
+    }
+#else
+    sparse_dists = query_db_sparse(ref_sketches, kmer_lengths, random,
+      jaccard, kNN, dist_col, num_threads);
+#endif
   }
-  Eigen::VectorXf dummy_query_ref;
-  Eigen::VectorXf dummy_query_query;
-  NumpyMatrix long_form = long_to_square(dists.col(dist_col), dummy_query_ref,
-                                         dummy_query_query, num_threads);
-  sparse_coo sparse_return = sparsify_dists(long_form, dist_cutoff, kNN);
 
-  return (sparse_return);
+  return sparse_dists;
 }
 
 void addRandomToDb(const std::string &db_name,
@@ -217,12 +242,13 @@ PYBIND11_MODULE(pp_sketchlib, m) {
         py::arg("jaccard") = false, py::arg("num_threads") = 1,
         py::arg("use_gpu") = false, py::arg("device_id") = 0);
 
-  m.def("queryDatabaseSparse", &sparseQuery,
+  m.def("querySelfSparse", &querySelfSparse,
         py::return_value_policy::reference_internal,
-        "Find distances between sketches; return a sparse matrix",
-        py::arg("ref_db_name"), py::arg("query_db_name"), py::arg("rList"),
-        py::arg("qList"), py::arg("klist"), py::arg("random_correct") = true,
-        py::arg("dist_cutoff") = 0, py::arg("kNN") = 0, py::arg("core") = true,
+        "Find self-self distances between sketches; return a sparse matrix",
+        py::arg("ref_db_name"), py::arg("rList"), py::arg("klist"), py::arg("random_correct") = true,
+        py::arg("jaccard") = false,
+        py::arg("kNN") = 0, py::arg("dist_cutoff") = 0.0f,
+        py::arg("dist_col") = 0,
         py::arg("num_threads") = 1, py::arg("use_gpu") = false,
         py::arg("device_id") = 0);
 
diff --git a/vendor/highfive/.clang-format b/vendor/highfive/.clang-format
new file mode 100644
index 00000000..eb2386bb
--- /dev/null
+++ b/vendor/highfive/.clang-format
@@ -0,0 +1,95 @@
+---
+Language:        Cpp
+# BasedOnStyle:  LLVM
+AccessModifierOffset: -2
+AlignAfterOpenBracket: Align
+AlignConsecutiveAssignments: false
+AlignConsecutiveDeclarations: false
+AlignEscapedNewlinesLeft: false
+AlignOperands:   true
+AlignTrailingComments: true
+AllowAllParametersOfDeclarationOnNextLine: true
+AllowShortBlocksOnASingleLine: false
+AllowShortCaseLabelsOnASingleLine: false
+AllowShortFunctionsOnASingleLine: Empty
+AllowShortIfStatementsOnASingleLine: false
+AllowShortLoopsOnASingleLine: false
+AlwaysBreakAfterDefinitionReturnType: None
+AlwaysBreakAfterReturnType: None
+AlwaysBreakBeforeMultilineStrings: false
+AlwaysBreakTemplateDeclarations: true
+BinPackArguments: true
+BinPackParameters: false
+BraceWrapping:
+  AfterClass:      false
+  AfterControlStatement: false
+  AfterEnum:       false
+  AfterFunction:   false
+  AfterNamespace:  false
+  AfterObjCDeclaration: false
+  AfterStruct:     false
+  AfterUnion:      false
+  BeforeCatch:     false
+  BeforeElse:      false
+  IndentBraces:    false
+BreakBeforeBinaryOperators: None
+BreakBeforeBraces: Attach
+BreakBeforeTernaryOperators: true
+BreakConstructorInitializersBeforeComma: true
+BreakAfterJavaFieldAnnotations: false
+BreakStringLiterals: true
+ColumnLimit:     90
+CommentPragmas:  '^ IWYU pragma:'
+ConstructorInitializerAllOnOneLineOrOnePerLine: false
+ConstructorInitializerIndentWidth: 4
+ContinuationIndentWidth: 4
+Cpp11BracedListStyle: true
+DerivePointerAlignment: false
+DisableFormat:   false
+ExperimentalAutoDetectBinPacking: false
+ForEachMacros:   [ foreach, Q_FOREACH, BOOST_FOREACH ]
+IncludeCategories:
+  - Regex:           '^"(llvm|llvm-c|clang|clang-c)/'
+    Priority:        2
+  - Regex:           '^(<|"(gtest|isl|json)/)'
+    Priority:        3
+  - Regex:           '.*'
+    Priority:        1
+IncludeIsMainRegex: '$'
+IndentCaseLabels: false
+IndentWidth:     4
+IndentWrappedFunctionNames: false
+JavaScriptQuotes: Leave
+JavaScriptWrapImports: true
+KeepEmptyLinesAtTheStartOfBlocks: true
+MacroBlockBegin: ''
+MacroBlockEnd:   ''
+MaxEmptyLinesToKeep: 1
+NamespaceIndentation: None
+ObjCBlockIndentWidth: 2
+ObjCSpaceAfterProperty: false
+ObjCSpaceBeforeProtocolList: true
+PenaltyBreakAssignment: 40
+PenaltyBreakBeforeFirstCallParameter: 19
+PenaltyBreakComment: 300
+PenaltyBreakFirstLessLess: 120
+PenaltyBreakString: 1000
+PenaltyExcessCharacter: 1000000
+PenaltyReturnTypeOnItsOwnLine: 60
+PointerAlignment: Left
+ReflowComments:  true
+SortIncludes:    true
+SpaceAfterCStyleCast: false
+SpaceBeforeAssignmentOperators: true
+SpaceBeforeParens: ControlStatements
+SpaceInEmptyParentheses: false
+SpacesBeforeTrailingComments: 2
+SpacesInAngles:  false
+SpacesInContainerLiterals: true
+SpacesInCStyleCastParentheses: false
+SpacesInParentheses: false
+SpacesInSquareBrackets: false
+Standard: Cpp11
+TabWidth: 4
+UseTab: Never
+...
diff --git a/vendor/highfive/.github/ISSUE_TEMPLATE/bug_report.md b/vendor/highfive/.github/ISSUE_TEMPLATE/bug_report.md
new file mode 100644
index 00000000..2558a56c
--- /dev/null
+++ b/vendor/highfive/.github/ISSUE_TEMPLATE/bug_report.md
@@ -0,0 +1,31 @@
+---
+name: Bug report
+about: Create a report to help us improve
+title: ''
+labels: ''
+assignees: ''
+
+---
+
+**Describe the bug**
+A clear and concise description of what the bug is.
+
+**To Reproduce**
+Steps to reproduce the behavior:
+1. Go to '...'
+2. Click on '....'
+3. Scroll down to '....'
+4. See error
+
+**Expected behavior**
+A clear and concise description of what you expected to happen.
+
+**Stacktrace**
+If applicable, add a stacktrace and error messages to help explain your problem.
+
+**Desktop (please complete the following information):**
+ - OS: [e.g. ubuntu 20.10, macos 10.15]
+ - Version [e.g. master branch]
+
+**Additional context**
+Add any other context about the problem here.
diff --git a/vendor/highfive/.github/ISSUE_TEMPLATE/feature_request.md b/vendor/highfive/.github/ISSUE_TEMPLATE/feature_request.md
new file mode 100644
index 00000000..4ead4805
--- /dev/null
+++ b/vendor/highfive/.github/ISSUE_TEMPLATE/feature_request.md
@@ -0,0 +1,20 @@
+---
+name: Feature request
+about: Suggest an idea for this project
+title: ''
+labels: ''
+assignees: ''
+
+---
+
+**Is your feature request related to a problem? Please describe.**
+A clear and concise description of what the problem is. Ex. I'm always frustrated when [...]
+
+**Describe the solution you'd like**
+A clear and concise description of what you want to happen.
+
+**Describe alternatives you've considered**
+A clear and concise description of any alternative solutions or features you've considered.
+
+**Additional context**
+Add any other context about the feature request here.
diff --git a/vendor/highfive/.github/build.sh b/vendor/highfive/.github/build.sh
new file mode 100644
index 00000000..65fe348d
--- /dev/null
+++ b/vendor/highfive/.github/build.sh
@@ -0,0 +1,16 @@
+# Build env
+
+[ "$CC" ] && $CC --version
+cmake --version
+set -x
+cmake -E make_directory $GITHUB_WORKSPACE/build
+cd $GITHUB_WORKSPACE/build
+cmake $GITHUB_WORKSPACE \
+  -DCMAKE_BUILD_TYPE=$BUILD_TYPE \
+  -DCMAKE_INSTALL_PREFIX=$INSTALL_DIR \
+  -DHIGHFIVE_USE_EIGEN:BOOL=TRUE \
+  "${CMAKE_OPTIONS[@]}"
+
+
+echo ">>> BUILDING"
+cmake --build . --config $BUILD_TYPE --parallel 2 --verbose
diff --git a/vendor/highfive/.github/pull_request_template.md b/vendor/highfive/.github/pull_request_template.md
new file mode 100644
index 00000000..58abc6c6
--- /dev/null
+++ b/vendor/highfive/.github/pull_request_template.md
@@ -0,0 +1,25 @@
+**Description**
+
+Please include a summary of the change and which issue is fixed or which feature is added.
+
+- [ ] Issue 1 fixed
+- [ ] Issue 2 fixed
+- [ ] Feature 1 added
+- [ ] Feature 2 added
+
+Fixes #(issue)
+
+**How to test this?**
+
+Please describe the tests that you ran to verify your changes. Provide instructions so we can reproduce if there is no integration test added with this PR. Please also list any relevant details for your test configuration
+
+```bash
+cmake ..
+make -j8
+make test
+```
+
+**Test System**
+ - OS: [e.g. Ubuntu 20.04]
+ - Compiler: [e.g. clang 12.0.0]
+ - Dependency versions: [e.g. hdf5 1.12]
diff --git a/vendor/highfive/.github/workflows/ci.yml b/vendor/highfive/.github/workflows/ci.yml
new file mode 100644
index 00000000..1c8d05f8
--- /dev/null
+++ b/vendor/highfive/.github/workflows/ci.yml
@@ -0,0 +1,147 @@
+name: HighFive_CI
+
+concurrency:
+  group: ${{ github.workflow }}#${{ github.ref }}
+  cancel-in-progress: true
+
+on:
+  push:
+    branches:
+      - ci_test
+      - release/**
+  pull_request:
+    branches:
+      - master
+      - release/**
+    paths-ignore:
+      - '**.md'
+      - '**.rst'
+      - 'doc/**'
+
+env:
+  HOMEBREW_NO_AUTO_UPDATE: 1  # for reproducibility, dont autoupdate
+  BUILD_TYPE: RelWithDebInfo
+  INSTALL_DIR: install
+
+jobs:
+
+  # Job testing compiling on several Linux systems + MPI
+  # =========================================================
+  Linux_MPI:
+    runs-on: ${{matrix.os}}
+    strategy:
+      matrix:
+        os: [ubuntu-16.04, ubuntu-18.04, ubuntu-20.04]
+
+    steps:
+    - uses: actions/checkout@v2
+    - uses: conda-incubator/setup-miniconda@v2
+
+    - name: "Install libraries"
+      shell: bash -l {0}  # required to activate conda
+      run: |
+        sudo apt-get -qq update
+        sudo apt-get -qq install libboost-all-dev libhdf5-openmpi-dev libeigen3-dev libsz2
+        conda install -y -c conda-forge xtl xsimd xtensor
+
+    - name: Build
+      shell: bash -l {0}  # required to activate conda
+      run: |
+        CMAKE_OPTIONS=(-DHIGHFIVE_PARALLEL_HDF5:BOOL=ON -DHIGHFIVE_USE_XTENSOR:BOOL=ON)
+        source $GITHUB_WORKSPACE/.github/build.sh
+
+    - name: Test
+      working-directory: ${{github.workspace}}/build
+      run: ctest --output-on-failure -C $BUILD_TYPE
+
+
+  # Job testing several compilers on a stable Linux
+  # ====================================================
+  Linux_Compilers:
+    runs-on: ubuntu-18.04
+    strategy:
+      matrix:
+        env: [
+          {CC: clang-9, CXX: clang++-9},
+          {CC: gcc-9, CXX: g++-9},
+          {CC: gcc-10, CXX: g++-10},
+        ]
+
+    steps:
+    - uses: actions/checkout@v2
+
+    - name: "Install libraries"
+      run: sudo apt-get update && sudo apt-get install libboost-all-dev libhdf5-dev libeigen3-dev libsz2
+
+    - name: Build
+      env: ${{matrix.env}}
+      run: source $GITHUB_WORKSPACE/.github/build.sh
+
+    - name: Test
+      working-directory: ${{github.workspace}}/build
+      run: ctest --output-on-failure -C $BUILD_TYPE
+
+
+  # Job testing in OSX
+  # =======================
+  OSX:
+    runs-on: macOS-10.15
+
+    steps:
+    - uses: actions/checkout@v2
+
+    - name: "Install libraries (OSX)"
+      run: brew install boost hdf5 eigen
+
+    - name: Build
+      run: source $GITHUB_WORKSPACE/.github/build.sh
+
+    - name: Test
+      working-directory: ${{github.workspace}}/build
+      run: ctest --output-on-failure -C $BUILD_TYPE
+
+
+  # Job testing in Windows
+  # ===========================
+  Windows:
+    runs-on: Windows-latest
+    steps:
+    - uses: actions/checkout@v2
+    - uses: conda-incubator/setup-miniconda@v2
+
+    - name: "Install libraries"
+      run: conda install -y -c conda-forge boost-cpp hdf5 eigen xtl xsimd xtensor
+
+    - name: Build
+      shell: bash -l {0}
+      run: |
+        CMAKE_OPTIONS=(-DHIGHFIVE_USE_XTENSOR:BOOL=ON)
+        source $GITHUB_WORKSPACE/.github/build.sh
+
+    - name: Test
+      working-directory: ${{github.workspace}}/build
+      shell: bash -l {0}
+      run: ctest --output-on-failure -C $BUILD_TYPE
+
+  # Job producing code coverage report
+  # ==================================
+  Code_coverage:
+    runs-on: macOS-10.15
+
+    steps:
+    - name: "Install libraries"
+      run: brew install boost hdf5 eigen lcov
+
+    - uses: actions/checkout@v2
+      with:
+        fetch-depth: 2
+
+    - name: Build and test for code coverage
+      run: |
+        CMAKE_OPTIONS=(-DHIGHFIVE_BUILD_DOCS:BOOL=FALSE -DCMAKE_CXX_FLAGS="-coverage -O0")
+        source $GITHUB_WORKSPACE/.github/build.sh
+        (cd $GITHUB_WORKSPACE;  lcov --capture  --initial --directory . --no-external --output-file build/coverage-base.info)
+        make test
+        (cd $GITHUB_WORKSPACE; lcov --capture  --directory . --no-external --output-file build/coverage-run.info)
+        lcov --add-tracefile coverage-base.info --add-tracefile coverage-run.info --output-file coverage-combined.info
+        bash <(curl -s https://codecov.io/bash) -f coverage-combined.info
diff --git a/vendor/highfive/.github/workflows/gh-pages.yml b/vendor/highfive/.github/workflows/gh-pages.yml
new file mode 100644
index 00000000..0615edca
--- /dev/null
+++ b/vendor/highfive/.github/workflows/gh-pages.yml
@@ -0,0 +1,49 @@
+name: gh-pages
+
+on:
+  push:
+    branches:
+      - master
+
+jobs:
+
+  publish:
+
+    runs-on: ubuntu-latest
+
+    defaults:
+      run:
+        shell: bash -l {0}
+
+    steps:
+
+    - name: Basic GitHub action setup
+      uses: actions/checkout@v2
+
+    - name: Set conda environment "test"
+      uses: conda-incubator/setup-miniconda@v2
+      with:
+        mamba-version: "*"
+        channels: conda-forge,defaults
+        channel-priority: true
+        environment-file: doc/environment.yaml
+        activate-environment: test
+        auto-activate-base: false
+
+    - name: Run doxygen
+      run: |
+        mkdir build
+        cd build
+        cmake .. -DHIGHFIVE_USE_BOOST=0
+        make doc
+
+    - name: Deploy to GitHub Pages
+      if: success()
+      uses: crazy-max/ghaction-github-pages@v2
+      with:
+        target_branch: gh-pages
+        build_dir: build/doc/html
+        jekyll: false
+        keep_history: false
+      env:
+        GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
diff --git a/vendor/highfive/.github/workflows/integration_trigger.yml b/vendor/highfive/.github/workflows/integration_trigger.yml
new file mode 100644
index 00000000..3fe65f32
--- /dev/null
+++ b/vendor/highfive/.github/workflows/integration_trigger.yml
@@ -0,0 +1,15 @@
+name: Integration Test Trigger
+on:
+  pull_request:
+    types: [closed]
+jobs:
+  merge-PR:
+    if: github.event.pull_request.merged == true
+    runs-on: ubuntu-latest
+    steps:
+      - name: Trigger integration tests on BlueBrain/HighFive-testing
+        run: |
+          curl -X POST https://api.github.com/repos/BlueBrain/HighFive-testing/dispatches \
+          -H 'Accept: application/vnd.github.everest-preview+json' \
+          -u ${{ secrets.ACCESS_TOKEN }} \
+          --data '{"event_type": "merge", "client_payload": { "repository": "'"$GITHUB_REPOSITORY"'" }}'
diff --git a/vendor/highfive/.gitignore b/vendor/highfive/.gitignore
new file mode 100644
index 00000000..75c4e044
--- /dev/null
+++ b/vendor/highfive/.gitignore
@@ -0,0 +1,5 @@
+build
+tests/test_project
+.idea
+
+.vs/
diff --git a/vendor/highfive/.gitmodules b/vendor/highfive/.gitmodules
new file mode 100644
index 00000000..3517c243
--- /dev/null
+++ b/vendor/highfive/.gitmodules
@@ -0,0 +1,3 @@
+[submodule "deps/hpc-coding-conventions"]
+	path = deps/hpc-coding-conventions
+	url = https://github.com/BlueBrain/hpc-coding-conventions.git
diff --git a/vendor/highfive/.travis.yml b/vendor/highfive/.travis.yml
new file mode 100644
index 00000000..bc5d3408
--- /dev/null
+++ b/vendor/highfive/.travis.yml
@@ -0,0 +1,138 @@
+# Adapted from various sources, including:
+# - Louis Dionne's Hana: https://github.com/ldionne/hana
+# - Paul Fultz II's FIT: https://github.com/pfultz2/Fit
+# - Eric Niebler's range-v3: https://github.com/ericniebler/range-v3
+# - Gabi Melman spdlog: https://github.com/gabime/spdlog
+
+sudo: required
+language: cpp
+
+addons: &gcc7
+  apt:
+    packages:
+      - g++-7
+      - libboost-all-dev
+      - libhdf5-openmpi-dev
+      - libeigen3-dev
+      - ninja-build
+    sources:
+      - ubuntu-toolchain-r-test
+
+matrix:
+  include:
+    # Older linux (trusty) with default gcc
+    # Install serial hdf5 + build serial
+    - os: linux
+      dist: trusty
+      env:
+        - HIGHFIVE_USE_XTENSOR=False
+        - HIGHFIVE_USE_OPENCV=False
+        - HIGHFIVE_PARALLEL_HDF5=False
+        - IS_BASE_ENVIRON=1
+      addons:
+        apt:
+          packages:
+            - libboost-all-dev
+            - libeigen3-dev
+            - libhdf5-serial-dev
+            - ninja-build
+
+    # Linux gcc-7
+    # Install parallel hdf5 + build parallel
+    - os: linux
+      dist: xenial
+      env:
+        - GCC_VERSION=7
+        - HIGHFIVE_USE_XTENSOR=True
+        - HIGHFIVE_USE_OPENCV=False
+        - HIGHFIVE_PARALLEL_HDF5=True
+      addons: *gcc7
+
+    # Mac OSX XCode 10
+    - os: osx
+      osx_image: xcode10.3
+      env:
+        - HIGHFIVE_USE_XTENSOR=True
+        - HIGHFIVE_USE_OPENCV=True
+        - HIGHFIVE_PARALLEL_HDF5=False
+
+    # Windows
+    - os: windows
+      env:
+        - HIGHFIVE_USE_XTENSOR=True
+        - HIGHFIVE_USE_OPENCV=True
+        - HIGHFIVE_PARALLEL_HDF5=False
+
+env:
+  global:
+    - MINCONDA_VERSION="latest"
+    - MINCONDA_LINUX="Linux-x86_64"
+    - MINCONDA_OSX="MacOSX-x86_64"
+
+install:
+  - export HOMEBREW_NO_AUTO_UPDATE=1  # for reproducibility, dont autoupdate
+
+  - if [[ "$TRAVIS_OS_NAME" == "linux" ]]; then
+      MINCONDA_OS=$MINCONDA_LINUX;
+    elif [[ "$TRAVIS_OS_NAME" == "osx" ]]; then
+      if [ "$BREW_USE_LATEST" ]; then
+        brew update;
+        brew install hdf5; brew upgrade hdf5;
+      fi;
+      brew install boost hdf5 eigen ninja;
+      MINCONDA_OS=$MINCONDA_OSX;
+    fi
+
+  - if [[ "$TRAVIS_OS_NAME" == "windows" ]]; then
+      export CMAKE_GENERATOR="Visual Studio 15 2017 Win64" ;
+      export TESTS_TARGET="RUN_TESTS";
+      choco install --yes miniconda3 ;
+      source C:/Tools/miniconda3/Scripts/activate ;
+    else
+      export CMAKE_GENERATOR="Ninja" ;
+      export TESTS_TARGET="test";
+      wget "http://repo.continuum.io/miniconda/Miniconda3-$MINCONDA_VERSION-$MINCONDA_OS.sh" -O miniconda.sh;
+      bash miniconda.sh -b -p $HOME/miniconda ;
+      source $HOME/miniconda/bin/activate;
+      hash -r ;
+    fi
+  - conda config --set always_yes yes --set changeps1 no
+  - conda update -q conda
+  - conda install -c conda-forge mamba
+  - if [[ "$HIGHFIVE_USE_XTENSOR" == "True" ]]; then
+      mamba install -c conda-forge xtl xsimd xtensor;
+    fi
+  - if [[ "$HIGHFIVE_USE_OPENCV" == "True" ]]; then
+      mamba install -c conda-forge libopencv opencv;
+    fi
+  - if [[ "$TRAVIS_OS_NAME" == "windows" ]]; then
+      mamba install -c conda-forge boost-cpp hdf5 eigen;
+    fi
+
+before_script:
+  - if [ -n "$GCC_VERSION" ]; then export CXX="g++-${GCC_VERSION}" CC="gcc-${GCC_VERSION}"; fi
+  - if [ -n "$CLANG_VERSION" ]; then export CXX="clang++-${CLANG_VERSION}" CC="clang-${CLANG_VERSION}"; fi
+  - if [[ "$TRAVIS_OS_NAME" == "osx" ]]; then export CXX="clang++" CC="clang"; fi
+  - which $CXX
+  - which $CC
+  - $CXX --version
+  - cmake --version
+
+script:
+  - cd ${TRAVIS_BUILD_DIR}
+  - mkdir -p build && pushd build
+  - >
+    cmake --warn-uninitialized --debug-output
+    -DCMAKE_VERBOSE_MAKEFILE:BOOL=ON
+    -DHIGHFIVE_TEST_SINGLE_INCLUDES:BOOL=ON
+    -DHIGHFIVE_PARALLEL_HDF5:BOOL=${HIGHFIVE_PARALLEL_HDF5}
+    -DHIGHFIVE_USE_EIGEN:BOOL=ON
+    -DHIGHFIVE_USE_XTENSOR:BOOL=${HIGHFIVE_USE_XTENSOR}
+    -DHIGHFIVE_USE_OPENCV:BOOL=${HIGHFIVE_USE_OPENCV}
+    -G "${CMAKE_GENERATOR}" ../
+  - cmake --build .
+  - CTEST_OUTPUT_ON_FAILURE=1 cmake --build . --target ${TESTS_TARGET}
+  - popd
+  - if [ $IS_BASE_ENVIRON ]; then
+      bash tests/test_project_integration.sh;
+    fi
diff --git a/vendor/highfive/CHANGELOG.md b/vendor/highfive/CHANGELOG.md
new file mode 100644
index 00000000..b722b8dc
--- /dev/null
+++ b/vendor/highfive/CHANGELOG.md
@@ -0,0 +1,152 @@
+## Version 2.3.1 - 2021-08-04
+### Improvements
+    - Clean cmake files from old code (#465)
+    - Adding path to type warning message (#471)
+    - Adding compound types example, w dataset and attr (#467)
+
+### Bug Fixes
+    - Resolve an issue where padding of nested compound types were being calculated incorrectly (#461) (#468)
+    - GHA: drop previous runs (#462)
+
+## Version 2.3 - 2021-05-07
+### New Features:
+    - Add SZIP support (#435)
+    - Add option *parents* to createDataSet (#425)
+    - Implementing getting the filename dynamically (#424)
+    - Ability to create soft and external links (#421)
+    - Generalizing getPath and adding getFile as PathTraits (#417)
+
+### Improvements:
+    - Unified reading/writing attributes and datasets (#450)
+    - Old compilers have been removed from docker image (#430)
+    - Cleaning up and improving property lists (#429)
+    - An example using hdf5 references (#396) (#397)
+    - Add all property lists alias for completeness (#427)
+    - Add property CreateIntermediateGroup (#423)
+    - Add code coverage through codecov.io (#420)
+    - Introducing GitHub Actions CI (#416)
+    - Create issue and PR templates (#412)
+    - Initialize SilenceHDF5 to true in _exist (#411)
+    - Generalizing xtensor API (#407)
+    - Minor doc updates (#409)
+    - Fixing minor error in GH Action (#408)
+    - Uploading docs to gh-pages using GitHub Actions (#403)
+    - Various minor documentation updates (#405)
+    - optional documentation building in cmake (#377)
+    - From can be automatic now (#384)
+    - get_dim_vector in inspector (#383)
+    - Put type_of_array in inspector (#382)
+    - Move array_dims in the future manipulator (#381)
+    - Unify interface of H5Attribute with H5Slice_traits (#378)
+    - Use std::move in NRVO depending of version of GCC (#375)
+    - Fixed typo '-DD' to '-D' in 'Dependencies'. (#371)
+    - Changing date format (#364)
+
+### Bug fixes:
+    - Fix use before initialization (#414)
+    - Adding CMake include guard (#389)
+
+## Version 2.2.2 - 2020-07-30
+### New Features:
+    - [H5Easy] Adding OpenCV support (#343)
+    - [H5Easy] Enabling compression & Adding attributes (#337)
+    - Adding missing function to H5Attribute (#337) 
+    - Add methods to retrieve Node paths or Dataset names and rename objects (#346)
+    - Add a file with the current version number of HighFive (#349)
+
+### Improvements
+    - [H5Easy] Updating error message dump (#335)
+    - [H5Easy] Switching implementation to partial specialization based on static dispatch (#327)
+    - Simplifying imports, new policy (#324)
+
+## Version 2.2.1 - 2020-04-28
+### Improvements
+    - Add a mechanism to not include target HighFive several times (#336)
+    - Fix SilenceHDF5 initialization for NodeTraits (#333)
+
+## Version 2.2 - 2020-03-23
+### New Features:
+    - Compound Types: API to register and read/write structs (#78). Thanks to Richard Shaw.
+    - Fixed-length strings. API via char[] and `FixedLenStringArray`(#277)
+    - Enum data types (#297)
+    - Datasets of HDF5 References. Support to dereference groups and datasets (#306)
+    - Objects (hard/soft link) can now be deleted with `unlink` (#284). Thanks to Tom Vander Aa.
+    - Attributes can be deleted with `deleteAttribute` (#239)
+
+### Improvements:
+    - `Attribute`s (metadata) now support additional types (#298)
+    - H5Easy: Reworked for compatibility with `Eigen::ref` and `Eigen::Map` (#291, #293)
+    - Hdf5 1.12 compatibility: working `Object::getInfo` and marking getAddress deprecated (#311)
+    - Strict compatibility with CMake 3.1 and C++11 (#304)
+    - CMake: Dependencies may be re-detected on FindPackage, fixed export targets and added integration tests (#255, #304, #312, #317)
+    - Support for array of `Eigen::Matrix` (#258)
+    - Selection: `ElementSet` working for N-dimensions (#247)
+
+### Bug Fixes:
+    - Shortcut syntax with c arrays (#273)
+    - Compatibility with in MSVC (Exception messages #263 and avoid throwing in `exist` check #308)
+
+## Version 2.1 - 2019-10-30
+### New Features:
+    - Inspection: API to get the type of links/objects and datasets data-types (#221)
+    - H5Easy: API for simple import/export to Eigen and xtensor (#141)
+    - Support for chunk and deflate configuration at dataset creation/open (#125). Added generic RawPropertyLists. (#157)
+    - Recursive `createGroup` and `exist` (#152)
+    - Shortcut syntax: ability to create a filled dataset in a single line (#130)
+    - DataSet now accepts `std::complex` and `std::array`'s (#128, #129)
+
+### Improvements:
+    - Improved compat with MSVC and ICC compilers
+    - CMake build system: modernized, create exported targets, better messages, etc.
+    - Building and publishing documentation: https://bluebrain.github.io/HighFive/
+    - Several other. See #231
+
+### Bug Fixes:
+    - Fixed header dependencies. They are now all include-able (#225)
+    - Fixed read/write of N-Dimensional data as nested vectors (#191)
+    - Fixed data broadcasting for reading (#136)
+
+## Version 2.0 - 2018-07-19
+    - First version with C++11 enforcement
+    - Support for property list
+    - Support for Chunking
+    - Support for Compression / Deflate
+    - Fix: missing move constructor for properties
+    - Fix: typo in MPI IO driver
+    - Fix: several typo fixes
+    - Fix: Add missing include
+
+## Version 1.5 - 2018-01-06
+    - SliceTraits::read split in two overloads, the first one for plain C arrays
+      and the second one for other types.
+    - Add support for complex number
+    - Add exist() method to the API
+    - Will be last release before 2.0 and enforcement of C++11
+
+## Version 1.4 - 2017-08-25
+	- Support id selection for the `select` function
+	- Suport STL containers of const elements
+	- Support scalar values and strings management
+	- Fix attribute assignment issue #40
+    - Fix Object assignment operator missing unref (possible memory leak )
+    - Introduce SilenceHDF5 for HDF5 error report
+    - Fix a unit test issue with SilenceHDF5
+
+## Version 1.3 - 2017-06-21
+    - Minor fixes
+
+## Version 1.2 - 2017-04-03
+	- Add Attribute support for Dataset
+	- Extend testing of Attribute support
+	- Fix issue related to multiple definitions in default driver
+	- Add more examples about attribute support
+
+## Version 1.1 - 2017-03-23
+    - Add support and examples for Parallel HDF5
+    - Initial implementation for H5 Properties
+    - Support for Attributes
+    - Improve documentation
+    - Add example for boost.Ublas matrix support
+
+## Version 1.0 - Init
+	- Initial release
diff --git a/vendor/highfive/CMake/HighFiveConfig.cmake.in b/vendor/highfive/CMake/HighFiveConfig.cmake.in
new file mode 100644
index 00000000..c37fffdf
--- /dev/null
+++ b/vendor/highfive/CMake/HighFiveConfig.cmake.in
@@ -0,0 +1,67 @@
+function(copy_interface_properties target source)
+  foreach(prop
+          INTERFACE_COMPILE_DEFINITIONS
+          INTERFACE_COMPILE_FEATURES
+          INTERFACE_COMPILE_OPTIONS
+          INTERFACE_INCLUDE_DIRECTORIES
+          INTERFACE_LINK_LIBRARIES
+          INTERFACE_SOURCES
+          INTERFACE_SYSTEM_INCLUDE_DIRECTORIES)
+    set_property(TARGET ${target} APPEND PROPERTY ${prop} $<TARGET_PROPERTY:${source},${prop}>)
+  endforeach()
+endfunction()
+
+if(TARGET HighFive)
+    return()
+endif()
+
+# Get HighFive targets
+include("${CMAKE_CURRENT_LIST_DIR}/HighFiveTargets.cmake")
+
+# Recreate combined HighFive
+add_library(HighFive INTERFACE IMPORTED)
+set_property(TARGET HighFive APPEND PROPERTY INTERFACE_COMPILE_DEFINITIONS MPI_NO_CPPBIND)  # No c++ bindings
+
+# Ensure we activate required C++ std
+if(NOT DEFINED CMAKE_CXX_STANDARD)
+  if(CMAKE_VERSION VERSION_LESS 3.8)
+    message(WARNING "HighFive requires minimum C++11. (C++14 for XTensor) \
+        You may need to set CMAKE_CXX_STANDARD in you project")
+  else()
+    # A client request for a higher std overrides this
+    target_compile_features(HighFive INTERFACE cxx_std_11)
+  endif()
+endif()
+
+# If the user sets this flag, all dependencies are preserved.
+# Useful in central deployments where dependencies are not prepared later
+set(HIGHFIVE_USE_INSTALL_DEPS @HIGHFIVE_USE_INSTALL_DEPS@ CACHE BOOL "Use original Highfive dependencies")
+if(HIGHFIVE_USE_INSTALL_DEPS)
+  # If enabled in the deploy config, request c++14
+  if(@HIGHFIVE_USE_XTENSOR@ AND NOT CMAKE_VERSION VERSION_LESS 3.8)
+    set_property(TARGET HighFive APPEND PROPERTY INTERFACE_COMPILE_FEATURES cxx_std_14)
+  endif()
+  message(STATUS "HIGHFIVE @PROJECT_VERSION@: Using original dependencies (HIGHFIVE_USE_INSTALL_DEPS=YES)")
+  copy_interface_properties(HighFive HighFive_HighFive)
+  return()
+endif()
+
+# When not using the pre-built dependencies, give user options
+if(DEFINED HIGHFIVE_USE_BOOST)
+  set(HIGHFIVE_USE_BOOST ${HIGHFIVE_USE_BOOST} CACHE BOOL "Enable Boost Support")
+else()
+  set(HIGHFIVE_USE_BOOST @HIGHFIVE_USE_BOOST@ CACHE BOOL "Enable Boost Support")
+endif()
+set(HIGHFIVE_USE_EIGEN "${HIGHFIVE_USE_EIGEN}" CACHE BOOL "Enable Eigen testing")
+set(HIGHFIVE_USE_XTENSOR "${HIGHFIVE_USE_XTENSOR}" CACHE BOOL "Enable xtensor testing")
+set(HIGHFIVE_PARALLEL_HDF5 @HIGHFIVE_PARALLEL_HDF5@ CACHE BOOL "Enable Parallel HDF5 support")
+
+if(HIGHFIVE_USE_XTENSOR AND NOT CMAKE_VERSION VERSION_LESS 3.8)
+  set_property(TARGET HighFive APPEND PROPERTY INTERFACE_COMPILE_FEATURES cxx_std_14)
+endif()
+
+message(STATUS "HIGHFIVE @PROJECT_VERSION@: (Re)Detecting Highfive dependencies (HIGHFIVE_USE_INSTALL_DEPS=NO)")
+include("${CMAKE_CURRENT_LIST_DIR}/HighFiveTargetDeps.cmake")
+foreach(dependency HighFive_libheaders libdeps)
+    copy_interface_properties(HighFive ${dependency})
+endforeach()
diff --git a/vendor/highfive/CMake/HighFiveTargetDeps.cmake b/vendor/highfive/CMake/HighFiveTargetDeps.cmake
new file mode 100644
index 00000000..51bc7b25
--- /dev/null
+++ b/vendor/highfive/CMake/HighFiveTargetDeps.cmake
@@ -0,0 +1,82 @@
+# Link against target system libs
+# -------------------------------
+
+if(NOT TARGET libdeps)
+
+  # Independent target to make it possible to have new dependencies each build
+  add_library(libdeps INTERFACE)
+
+  # HDF5
+  if(NOT DEFINED HDF5_C_LIBRARIES)
+    set(HDF5_NO_FIND_PACKAGE_CONFIG_FILE TRUE)  # Consistency
+    set(HDF5_PREFER_PARALLEL ${HIGHFIVE_PARALLEL_HDF5})
+    find_package(HDF5 REQUIRED)
+  endif()
+
+  if(HIGHFIVE_PARALLEL_HDF5 AND NOT HDF5_IS_PARALLEL)
+    message(WARNING "Parallel HDF5 requested but libhdf5 doesnt support it")
+  endif()
+
+  target_include_directories(libdeps SYSTEM INTERFACE ${HDF5_INCLUDE_DIRS})
+  target_link_libraries(libdeps INTERFACE ${HDF5_C_LIBRARIES})
+  target_compile_definitions(libdeps INTERFACE ${HDF5_DEFINITIONS})
+
+  # Boost
+  if(HIGHFIVE_USE_BOOST)
+    set(Boost_NO_BOOST_CMAKE TRUE)  # Consistency
+    find_package(Boost REQUIRED COMPONENTS system serialization)
+    # Dont use imported targets yet, not avail before cmake 3.5
+    target_include_directories(libdeps SYSTEM INTERFACE ${Boost_INCLUDE_DIR})
+    target_compile_definitions(libdeps INTERFACE BOOST_ALL_NO_LIB H5_USE_BOOST)
+  endif()
+
+  # Eigen
+  if(HIGHFIVE_USE_EIGEN)
+    if (NOT EIGEN3_INCLUDE_DIRS)
+      find_package(Eigen3 NO_MODULE)
+      if(Eigen3_FOUND)
+        message(STATUS "Found Eigen ${Eigen3_VERSION}: ${EIGEN3_INCLUDE_DIRS}")
+      else()
+        find_package(PkgConfig)
+        pkg_check_modules(EIGEN3 REQUIRED eigen3)
+      endif()
+    endif()
+    if (NOT EIGEN3_INCLUDE_DIRS)
+      message(FATAL_ERROR "Eigen was requested but could not be found")
+    endif()
+    target_include_directories(libdeps SYSTEM INTERFACE ${EIGEN3_INCLUDE_DIRS})
+    target_compile_definitions(libdeps INTERFACE H5_USE_EIGEN)
+  endif()
+
+  # xtensor
+  if(HIGHFIVE_USE_XTENSOR)
+    if (NOT xtensor_INCLUDE_DIRS)
+      find_package(xtensor REQUIRED)
+    endif()
+    target_include_directories(libdeps SYSTEM INTERFACE ${xtensor_INCLUDE_DIRS})
+    target_compile_definitions(libdeps INTERFACE H5_USE_XTENSOR)
+  endif()
+
+  # OpenCV
+  if(HIGHFIVE_USE_OPENCV)
+    if (NOT OpenCV_INCLUDE_DIRS)
+      find_package(OpenCV REQUIRED)
+    endif()
+    target_include_directories(libdeps SYSTEM INTERFACE ${OpenCV_INCLUDE_DIRS})
+    target_link_libraries(libdeps INTERFACE ${OpenCV_LIBS})
+    target_compile_definitions(libdeps INTERFACE H5_USE_OPENCV)
+  endif()
+
+  # MPI
+  if(HIGHFIVE_PARALLEL_HDF5 OR HDF5_IS_PARALLEL)
+    find_package(MPI REQUIRED)
+    target_include_directories(libdeps SYSTEM INTERFACE ${MPI_CXX_INCLUDE_PATH})
+    target_link_libraries(libdeps INTERFACE ${MPI_CXX_LIBRARIES})
+    if(CMAKE_VERSION VERSION_LESS 3.13)
+      target_link_libraries(libdeps INTERFACE ${MPI_CXX_LINK_FLAGS})
+    else()
+      target_link_options(libdeps INTERFACE "SHELL:${MPI_CXX_LINK_FLAGS}")
+    endif()
+  endif()
+
+endif()
diff --git a/vendor/highfive/CMake/HighFiveTargetExport.cmake b/vendor/highfive/CMake/HighFiveTargetExport.cmake
new file mode 100644
index 00000000..9906f395
--- /dev/null
+++ b/vendor/highfive/CMake/HighFiveTargetExport.cmake
@@ -0,0 +1,48 @@
+
+# Define the HighFive INTERFACE library
+add_library(libheaders INTERFACE)
+
+target_include_directories(libheaders INTERFACE
+  "$<BUILD_INTERFACE:${PROJECT_SOURCE_DIR}/include>"
+  "$<INSTALL_INTERFACE:include>")
+
+# Combined HighFive
+add_library(HighFive INTERFACE)
+target_compile_definitions(HighFive INTERFACE MPI_NO_CPPBIND)  # No c++ bindings
+target_link_libraries(HighFive INTERFACE libheaders libdeps)
+
+
+# Generate ${PROJECT_NAME}Config.cmake
+
+include(CMakePackageConfigHelpers)
+configure_package_config_file(${CMAKE_CURRENT_LIST_DIR}/HighFiveConfig.cmake.in
+  ${PROJECT_BINARY_DIR}/${PROJECT_NAME}Config.cmake
+  INSTALL_DESTINATION share/${PROJECT_NAME}/CMake)
+
+write_basic_package_version_file(
+    ${PROJECT_NAME}ConfigVersion.cmake
+    VERSION ${PROJECT_VERSION}
+    COMPATIBILITY AnyNewerVersion)
+
+install(FILES
+    CMake/HighFiveTargetDeps.cmake
+    ${PROJECT_BINARY_DIR}/${PROJECT_NAME}Config.cmake
+    ${PROJECT_BINARY_DIR}/${PROJECT_NAME}ConfigVersion.cmake
+  DESTINATION share/${PROJECT_NAME}/CMake)
+
+
+# Provides IMPORTED targets when using this project from build/install trees.
+
+# Specify targets to include in the HighFive Exports
+install(TARGETS HighFive libheaders libdeps
+        EXPORT HighFiveTargets)
+
+# Generate & install the Export for the INSTALL_INTERFACE
+install(EXPORT HighFiveTargets
+        NAMESPACE HighFive_
+        FILE HighFiveTargets.cmake
+        DESTINATION share/${PROJECT_NAME}/CMake)
+
+# Generate the Export for the BUILD_INTERACE (hardly used)
+export(EXPORT HighFiveTargets
+       FILE "${PROJECT_BINARY_DIR}/HighFiveTargets.cmake")
diff --git a/vendor/highfive/CMake/config/CompilerFlagsHelpers.cmake b/vendor/highfive/CMake/config/CompilerFlagsHelpers.cmake
new file mode 100644
index 00000000..e3755ad8
--- /dev/null
+++ b/vendor/highfive/CMake/config/CompilerFlagsHelpers.cmake
@@ -0,0 +1,59 @@
+# CompilerFlagsHelpers.cmake
+#
+# set of Convenience functions for portable compiler flags
+#
+# License: BSD 3
+#
+# Copyright (c) 2016, Adrien Devresse
+# All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
+#
+# 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
+#
+# 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
+#
+# 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission.
+#
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+
+
+set(SUPPORTED_COMPILER_LANGUAGE_LIST "C;CXX")
+
+foreach(COMPILER_LANGUAGE ${SUPPORTED_COMPILER_LANGUAGE_LIST})
+
+	# XLC compiler
+	if(CMAKE_${COMPILER_LANGUAGE}_COMPILER_ID MATCHES "XL")
+
+		# XLC -qinfo=all is awfully verbose on any platforms that use the GNU STL
+		# Enable by default only the relevant one
+		set(CMAKE_${COMPILER_LANGUAGE}_WARNING_ALL "-qformat=all -qinfo=lan:trx:ret:zea:cmp:ret")
+
+	## GCC, CLANG, rest of the world
+	elseif(CMAKE_${COMPILER_LANGUAGE}_COMPILER_ID MATCHES "Clang"
+        OR CMAKE_${COMPILER_LANGUAGE}_COMPILER_ID MATCHES "GNU"
+        OR CMAKE_${COMPILER_LANGUAGE}_COMPILER_ID MATCHES "Intel")
+		set(CMAKE_${COMPILER_LANGUAGE}_WARNING_ALL " -Wall -Wextra")
+		string(CONCAT CMAKE_${COMPILER_LANGUAGE}_WARNING_DEBUG
+			" -Werror -Wshadow -Wnon-virtual-dtor -Wunused -Woverloaded-virtual"
+			" -Wformat=2 -Wconversion -Wsign-conversion -Wno-error=deprecated-declarations"
+		)
+		if(NOT CMAKE_${COMPILER_LANGUAGE}_COMPILER_IS_ICC)
+			string(CONCAT CMAKE_${COMPILER_LANGUAGE}_WARNING_DEBUG
+				${CMAKE_${COMPILER_LANGUAGE}_WARNING_DEBUG}
+				" -Wpedantic -Wcast-align -Wdouble-promotion"
+			)
+		endif()
+	endif()
+
+    if(CMAKE_${COMPILER_LANGUAGE}_COMPILER_ID MATCHES "GNU"
+            AND (CMAKE_${COMPILER_LANGUAGE}_COMPILER_VERSION VERSION_GREATER "4.7.0"))
+        set(CMAKE_${COMPILER_LANGUAGE}_WARNING_ALL
+                "${CMAKE_${COMPILER_LANGUAGE}_WARNING_ALL} -Wno-unused-parameter")
+    endif()
+
+endforeach()
+
+
+
diff --git a/vendor/highfive/CMake/config/ReleaseDebugAutoFlags.cmake b/vendor/highfive/CMake/config/ReleaseDebugAutoFlags.cmake
new file mode 100644
index 00000000..2b980698
--- /dev/null
+++ b/vendor/highfive/CMake/config/ReleaseDebugAutoFlags.cmake
@@ -0,0 +1,45 @@
+# ReleaseDebugAutoFlags.cmake
+# 
+# Release / Debug configuration helper
+#
+# License: BSD 3
+#
+# Copyright (c) 2016, Adrien Devresse
+# All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
+#
+# 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
+#
+# 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
+#
+# 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission.
+#
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 
+
+
+
+## default configuration
+if(NOT CMAKE_BUILD_TYPE AND (NOT CMAKE_CONFIGURATION_TYPES))
+  set(CMAKE_BUILD_TYPE RelWithDebInfo CACHE STRING "Choose the type of build." FORCE)
+  message(STATUS "Setting build type to '${CMAKE_BUILD_TYPE}' as none was specified.")
+endif()
+
+
+# Different configuration types:
+#
+# Debug : Optimized for debugging, include symbols
+# Release : Release mode, no debuginfo 
+# RelWithDebInfo : Distribution mode, basic optimizations for potable code with debuginfos
+
+include(CompilerFlagsHelpers)
+
+
+string(APPEND CMAKE_C_FLAGS_RELEASE  "${CMAKE_C_WARNING_ALL}")
+string(APPEND CMAKE_C_FLAGS_DEBUG  "${CMAKE_C_WARNING_ALL}" "${CMAKE_C_WARNING_DEBUG}")
+string(APPEND CMAKE_C_FLAGS_RELWITHDEBINFO "${CMAKE_C_WARNING_ALL}" "${CMAKE_C_WARNING_DEBUG}")
+
+
+string(APPEND CMAKE_CXX_FLAGS_RELEASE  "${CMAKE_CXX_WARNING_ALL}")
+string(APPEND CMAKE_CXX_FLAGS_DEBUG  "${CMAKE_CXX_WARNING_ALL}" "${CMAKE_CXX_WARNING_DEBUG}")
+string(APPEND CMAKE_CXX_FLAGS_RELWITHDEBINFO "${CMAKE_CXX_WARNING_ALL}" "${CMAKE_C_WARNING_DEBUG}")
diff --git a/vendor/highfive/CMake/config/TestHelpers.cmake b/vendor/highfive/CMake/config/TestHelpers.cmake
new file mode 100644
index 00000000..f3ca1cb7
--- /dev/null
+++ b/vendor/highfive/CMake/config/TestHelpers.cmake
@@ -0,0 +1,113 @@
+# TestHelpers.cmake
+# 
+# set of Convenience functions for unit testing with cmake
+#
+# License: BSD 3
+#
+# Copyright (c) 2016, Adrien Devresse
+# All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
+#
+# 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
+#
+# 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
+#
+# 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission.
+#
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 
+
+
+
+##
+# enable or disable detection of SLURM and MPIEXEC
+option(AUTO_TEST_WITH_SLURM "automatically add srun as test prefix in a SLURM environment" TRUE)
+option(AUTO_TEST_WITH_MPIEXEC "automatically add mpiexec as test prefix in a MPICH2/OpenMPI environment" TRUE)
+
+###
+##
+## Basic SLURM support
+## the prefix "srun" is added to any test in the environment
+## For a slurm test execution, simply run "salloc [your_exec_parameters] ctest"
+##    
+##
+if(AUTO_TEST_WITH_SLURM)
+    if(NOT DEFINED SLURM_SRUN_COMMAND)
+        find_program(SLURM_SRUN_COMMAND
+                       NAMES "srun"
+                       HINTS "${SLURM_ROOT}/bin")
+    endif()
+    
+    if(SLURM_SRUN_COMMAND)
+        set(TEST_EXEC_PREFIX_DEFAULT "${SLURM_SRUN_COMMAND}")
+        set(TEST_MPI_EXEC_PREFIX_DEFAULT "${SLURM_SRUN_COMMAND}")
+        set(TEST_MPI_EXEC_BIN_DEFAULT "${SLURM_SRUN_COMMAND}")
+	set(TEST_WITH_SLURM ON)
+        message(STATUS " - AUTO_TEST_WITH_SLURM with slurm cmd ${TEST_EXEC_PREFIX_DEFAULT} ")
+        message(STATUS "  -- set test execution prefix to ${TEST_EXEC_PREFIX_DEFAULT} ")
+        message(STATUS "  -- set MPI test execution prefix to ${TEST_MPI_EXEC_PREFIX_DEFAULT} ")
+    endif()
+
+endif()
+
+###
+## Basic MPIExec support, will just forward mpiexec as prefix
+## 
+if(AUTO_TEST_WITH_MPIEXEC AND NOT TEST_WITH_SLURM)
+
+   if(NOT DEFINED MPIEXEC)
+        find_program(MPIEXEC
+                     NAMES "mpiexec"
+                     HINTS "${MPI_ROOT}/bin")
+   endif()
+
+
+   if(MPIEXEC)
+        set(TEST_MPI_EXEC_PREFIX_DEFAULT "${MPIEXEC}")
+        set(TEST_MPI_EXEC_BIN_DEFAULT "${MPIEXEC}")
+	set(TEST_WITH_MPIEXEC ON)
+        message(STATUS " - AUTO_TEST_WITH_MPIEXEC cmd ${MPIEXEC} ")
+        message(STATUS "  -- set MPI test execution prefix to ${TEST_MPI_EXEC_PREFIX_DEFAULT} ")
+
+   endif()
+
+endif()
+
+
+
+###
+##  MPI executor program path without arguments used for testing.
+##  default: srun or mpiexec if found
+##
+set(TEST_MPI_EXEC_BIN "${TEST_MPI_EXEC_BIN_DEFAULT}" CACHE STRING "path of the MPI executor (mpiexec, mpirun) for test execution")
+
+
+
+###
+## Test execution prefix. Override this variable for any execution prefix required in clustered environment
+## 
+## To specify manually a command with argument, e.g -DTEST_EXEC_PREFIX="/var/empty/bin/srun;-n;-4" for a srun execution
+## with 4 nodes
+##
+## default: srun if found
+##
+set(TEST_EXEC_PREFIX "${TEST_EXEC_PREFIX_DEFAULT}" CACHE STRING "prefix command for the test executions")
+
+
+
+###
+## Test execution prefix specific for MPI programs.
+## 
+## To specify manually a command with argument, use the cmake list syntax. e.g -DTEST_EXEC_PREFIX="/var/empty/bin/mpiexec;-n;-4" for an MPI execution
+## with 4 nodes
+##
+## default: srun or mpiexec if found
+##
+set(TEST_MPI_EXEC_PREFIX "${TEST_MPI_EXEC_PREFIX_DEFAULT}" CACHE STRING "prefix command for the MPI test executions")
+
+
+
+
+
+
+
diff --git a/vendor/highfive/CMakeLists.txt b/vendor/highfive/CMakeLists.txt
new file mode 100644
index 00000000..9d723845
--- /dev/null
+++ b/vendor/highfive/CMakeLists.txt
@@ -0,0 +1,116 @@
+cmake_minimum_required(VERSION 3.1)
+if(${CMAKE_VERSION} VERSION_LESS 3.13)
+  cmake_policy(VERSION ${CMAKE_MAJOR_VERSION}.${CMAKE_MINOR_VERSION})
+else()
+  cmake_policy(VERSION 3.13)
+endif()
+
+project(HighFive VERSION 2.3.1)
+
+configure_file(${CMAKE_CURRENT_SOURCE_DIR}/include/highfive/H5Version.hpp.in
+               ${CMAKE_CURRENT_BINARY_DIR}/include/highfive/H5Version.hpp)
+# INCLUDES
+list(APPEND CMAKE_MODULE_PATH
+  ${PROJECT_SOURCE_DIR}/CMake
+  ${PROJECT_SOURCE_DIR}/CMake/portability
+  ${PROJECT_SOURCE_DIR}/CMake/config)
+
+include(CompilerFlagsHelpers)
+include(ReleaseDebugAutoFlags)
+
+# OPTIONS
+# Compat within Highfive 2.x series
+set(USE_BOOST ON CACHE BOOL "Enable Boost Support")
+set(USE_EIGEN OFF CACHE BOOL "Enable Eigen testing")
+set(USE_XTENSOR OFF CACHE BOOL "Enable xtensor testing")
+set(USE_OPENCV OFF CACHE BOOL "Enable OpenCV testing")
+mark_as_advanced(USE_BOOST USE_EIGEN USE_XTENSOR)
+
+option(HIGHFIVE_USE_BOOST "Enable Boost Support" ${USE_BOOST})
+option(HIGHFIVE_USE_EIGEN "Enable Eigen testing" ${USE_EIGEN})
+option(HIGHFIVE_USE_XTENSOR "Enable xtensor testing" ${USE_XTENSOR})
+option(HIGHFIVE_USE_OPENCV "Enable OpenCV testing" ${USE_OPENCV})
+option(HIGHFIVE_UNIT_TESTS "Enable unit tests" ON)
+option(HIGHFIVE_EXAMPLES "Compile examples" ON)
+option(HIGHFIVE_PARALLEL_HDF5 "Enable Parallel HDF5 support" OFF)
+option(HIGHFIVE_BUILD_DOCS "Enable documentation building" ON)
+
+# In deployments we probably don't want/cant have dynamic dependencies
+option(HIGHFIVE_USE_INSTALL_DEPS "End applications by default use detected dependencies here" OFF)
+mark_as_advanced(HIGHFIVE_USE_INSTALL_DEPS)
+
+
+# Check compiler cxx_std requirements
+# -----------------------------------
+
+if(CMAKE_CXX_STANDARD EQUAL 98)
+    message(FATAL_ERROR "HighFive needs to be compiled with at least C++11")
+endif()
+
+if(NOT DEFINED CMAKE_CXX_STANDARD)
+    set(CMAKE_CXX_STANDARD 11)
+    set(CMAKE_CXX_STANDARD_REQUIRED ON)
+    set(CMAKE_CXX_EXTENSIONS OFF)
+endif()
+
+if(HIGHFIVE_USE_XTENSOR)
+    set(CMAKE_CXX_STANDARD 14)
+    set(CMAKE_CXX_STANDARD_REQUIRED ON)
+endif()
+
+
+# Search dependencies (hdf5, boost, eigen, xtensor, mpi) and build target highfive_deps
+include(${PROJECT_SOURCE_DIR}/CMake/HighFiveTargetDeps.cmake)
+
+# Set-up HighFive to be used in 3rd party project using exports. Create a HighFive target
+include(${PROJECT_SOURCE_DIR}/CMake/HighFiveTargetExport.cmake)
+
+# Installation of headers (HighFive is only interface)
+install(DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/include/
+  DESTINATION "include"
+  PATTERN "*.in" EXCLUDE)
+
+# Installation of configured headers
+install(DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/include/
+  DESTINATION "include")
+
+
+# Preparing local building (tests, examples)
+# ------------------------------------------
+
+# Disable test if Boost was expressly disabled, or if HighFive is a sub-project
+if (NOT HIGHFIVE_USE_BOOST OR NOT CMAKE_CURRENT_SOURCE_DIR STREQUAL CMAKE_SOURCE_DIR)
+  if(HIGHFIVE_UNIT_TESTS)
+    message(WARNING "Unit tests have been DISABLED. (HIGHFIVE_USE_BOOST: ${HIGHFIVE_USE_BOOST})")
+  endif()
+  set(HIGHFIVE_UNIT_TESTS FALSE)
+endif()
+
+if(HIGHFIVE_UNIT_TESTS)
+  set(Boost_NO_BOOST_CMAKE TRUE)  # Consistency
+  find_package(Boost COMPONENTS system serialization unit_test_framework)
+  if (NOT Boost_FOUND)
+    message(FATAL_ERROR "\
+      Boost not found which is required for efficient multi-dimension and tests.\n\
+      To disable support please use cmake .. -DHIGHFIVE_USE_BOOST=OFF.")
+  endif()
+endif()
+
+
+if(CMAKE_CXX_COMPILER_ID MATCHES "Intel")
+  # ICC gets mad if we shorten "int"s
+  add_definitions("-wd1682")
+endif()
+
+if(HIGHFIVE_EXAMPLES)
+  add_subdirectory(src/examples)
+endif()
+
+if(HIGHFIVE_UNIT_TESTS)
+  enable_testing()
+  add_subdirectory(tests/unit)
+endif()
+
+if(HIGHFIVE_BUILD_DOCS)
+  add_subdirectory(doc)
+endif()
diff --git a/vendor/highfive/LICENSE b/vendor/highfive/LICENSE
new file mode 100644
index 00000000..bc1edcab
--- /dev/null
+++ b/vendor/highfive/LICENSE
@@ -0,0 +1,25 @@
+Boost Software License - Version 1.0 - August 17th, 2003
+
+Permission is hereby granted, free of charge, to any person or organization
+obtaining a copy of the software and accompanying documentation covered by
+this license (the "Software") to use, reproduce, display, distribute,
+execute, and transmit the Software, and to prepare derivative works of the
+Software, and to permit third-parties to whom the Software is furnished to
+do so, all subject to the following:
+
+The copyright notices in the Software and this entire statement, including
+the above license grant, this restriction and the following disclaimer,
+must be included in all copies of the Software, in whole or in part, and
+all derivative works of the Software, unless such copies or derivative
+works are solely in the form of machine-executable object code generated by
+a source language processor.
+
+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, TITLE AND NON-INFRINGEMENT. IN NO EVENT
+SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE
+FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE,
+ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+DEALINGS IN THE SOFTWARE.
+
+
diff --git a/vendor/highfive/README.md b/vendor/highfive/README.md
new file mode 100644
index 00000000..c969d6e3
--- /dev/null
+++ b/vendor/highfive/README.md
@@ -0,0 +1,204 @@
+# HighFive - HDF5 header-only C++ Library
+
+[![Build Status](https://travis-ci.org/BlueBrain/HighFive.svg?branch=master)](https://travis-ci.org/BlueBrain/HighFive)
+[![Coverity Statys](https://scan.coverity.com/projects/13635/badge.svg)](https://scan.coverity.com/projects/highfive)
+[![Doxygen -> gh-pages](https://github.com/BlueBrain/HighFive/workflows/gh-pages/badge.svg)](https://BlueBrain.github.io/HighFive)
+[![codecov](https://codecov.io/gh/BlueBrain/HighFive/branch/master/graph/badge.svg?token=UBKxHEn7RS)](https://codecov.io/gh/BlueBrain/HighFive)
+
+## Brief
+
+HighFive is a modern header-only C++11 friendly interface for libhdf5.
+
+HighFive supports STL vector/string, Boost::UBLAS, Boost::Multi-array, Eigen and Xtensor. It handles C++ from/to HDF5 with automatic type mapping.
+HighFive does not require additional libraries (see dependencies) and supports both HDF5 thread safety and Parallel HDF5 (contrary to the official hdf5 cpp)
+
+It integrates nicely with other CMake projects by defining (and exporting) a HighFive target.
+
+
+### Design
+- Simple C++-ish minimalist interface
+- No other dependency than libhdf5
+- Zero overhead
+- Support C++11
+
+### Feature support
+- create/read/write files, datasets, attributes, groups, dataspaces.
+- automatic memory management / ref counting
+- automatic conversion of `std::vector` and nested `std::vector` from/to any dataset with basic types
+- automatic conversion of `std::string` to/from variable length string dataset
+- selection() / slice support
+- parallel Read/Write operations from several nodes with Parallel HDF5
+- Advanced types: Compound, Enum, Arrays of Fixed-length strings, References
+- etc... (see [ChangeLog](./CHANGELOG.md))
+
+### Dependencies
+- hdf5 (dev)
+- hdf5-mpi (optional, opt-in with -D*HIGHFIVE_PARALLEL_HDF5*=ON)
+- boost >= 1.41 (recommended, opt-out with -D*HIGHFIVE_USE_BOOST*=OFF)
+- eigen3 (optional, opt-in with -D*HIGHFIVE_USE_EIGEN*=ON)
+- xtensor (optional, opt-in with -D*HIGHFIVE_USE_XTENSOR*=ON)
+
+
+## Examples
+
+#### Write a std::vector<int> to 1D HDF5 dataset and read it back
+
+```c++
+#include <highfive/H5File.hpp>
+
+using namespace HighFive;
+// we create a new hdf5 file
+File file("/tmp/new_file.h5", File::ReadWrite | File::Create | File::Truncate);
+
+std::vector<int> data(50, 1);
+
+// let's create a dataset of native integer with the size of the vector 'data'
+DataSet dataset = file.createDataSet<int>("/dataset_one",  DataSpace::From(data));
+
+// let's write our vector of int to the HDF5 dataset
+dataset.write(data);
+
+// read back
+std::vector<int> result;
+dataset.read(result);
+```
+
+**Note:** `H5File.hpp` is the top-level header of HighFive core which should be always included.
+
+**Note:** If you can use `DataSpace::From` on your data, you can combine the create and write into one statement.
+Such shortcut syntax is available for both `createDataSet` and `createAttribute`.
+```c++
+DataSet dataset = file.createDataSet("/dataset_one",  data);
+```
+
+#### Write a 2 dimensional C double float array to a 2D HDF5 dataset
+
+See [create_dataset_double.cpp](src/examples/create_dataset_double.cpp)
+
+#### Write and read a matrix of double float (boost::ublas) to a 2D HDF5 dataset
+
+See [boost_ublas_double.cpp](src/examples/boost_ublas_double.cpp)
+
+#### Write and read a subset of a 2D double dataset
+
+See [select_partial_dataset_cpp11.cpp](src/examples/select_partial_dataset_cpp11.cpp)
+
+#### Create, write and list HDF5 attributes
+
+See [create_attribute_string_integer.cpp](src/examples/create_attribute_string_integer.cpp)
+
+#### And others
+
+See [src/examples/](src/examples/) subdirectory for more info.
+
+
+### Compiling with HighFive
+
+```bash
+c++ -o program -I/path/to/highfive/include source.cpp  -lhdf5
+```
+
+### H5Easy
+
+For several 'standard' use cases the [highfive/H5Easy.hpp](include/highfive/H5Easy.hpp) interface is available. It allows:
+
+* Reading/writing in a single line of:
+
+    - scalars (to/from an extendible DataSet),
+    - strings,
+    - vectors (of standard types),
+    - [Eigen::Matrix](http://eigen.tuxfamily.org) (optional, enable CMake option `HIGHFIVE_USE_EIGEN`),
+    - [xt::xarray](https://github.com/QuantStack/xtensor) and [xt::xtensor](https://github.com/QuantStack/xtensor)
+      (optional, enable CMake option `HIGHFIVE_USE_XTENSOR`).
+    - [cv::Mat_](https://docs.opencv.org/master/df/dfc/classcv_1_1Mat__.html)
+      (optional, enable CMake option `HIGHFIVE_USE_OPENCV`).
+
+* Getting in a single line:
+
+    - the size of a DataSet,
+    - the shape of a DataSet.
+
+#### Example
+
+```cpp
+#include <highfive/H5Easy.hpp>
+
+int main() {
+    H5Easy::File file("example.h5", H5Easy::File::Overwrite);
+
+    int A = ...;
+    H5Easy::dump(file, "/path/to/A", A);
+
+    A = H5Easy::load<int>(file, "/path/to/A");
+}
+```
+
+whereby the `int` type of this example can be replaced by any of the above types. See [easy_load_dump.cpp](src/examples/easy_load_dump.cpp) for more details.
+
+**Note:** Classes such as `H5Easy::File` are just short for the regular `HighFive` classes (in this case `HighFive::File`). They can thus be used interchangeably.
+
+
+## CMake integration
+
+HighFive can easily be used by other C++ CMake projects.
+
+You may use HighFive from a folder in your project (typically a git submodule).
+```cmake
+cmake_minimum_required(VERSION 3.1 FATAL_ERROR)
+project(foo)
+set(CMAKE_CXX_STANDARD 11)
+
+add_subdirectory(highfive_folder)
+add_executable(bar bar.cpp)
+target_link_libraries(bar HighFive)
+```
+
+Alternativelly you can install HighFive once and use it in several projects via `find_package()`.
+
+A HighFive target will bring the compilation settings to find HighFive headers and all chosen dependencies.
+
+```cmake
+# ...
+find_package(HighFive REQUIRED)
+add_executable(bar bar.cpp)
+target_link_libraries(bar HighFive)
+```
+**Note:** Like with other libraries you may need to provide CMake the location to find highfive: `CMAKE_PREFIX_PATH=<highfive_install_dir>`
+
+**Note:** `find_package(HighFive)` will search dependencies as well (e.g. Boost if requested). In order to use the same dependencies found at HighFive install time (e.g. for system deployments) you may set `HIGHFIVE_USE_INSTALL_DEPS=YES`
+
+### Installing
+When installing via CMake, besides the headers, a HighFiveConfig.cmake is generated which provides the HighFive target, as seen before. Note: You may need to set `CMAKE_INSTALL_PREFIX`:
+```bash
+mkdir build && cd build
+# Look up HighFive CMake options, consider inspecting with `ccmake`
+cmake .. -DHIGHFIVE_EXAMPLES=OFF -DCMAKE_INSTALL_PREFIX="<highfive_install_dir>"
+make install
+```
+
+### Test Compilation
+As a header-only library, HighFive doesn't require compilation. You may however build tests and examples.
+
+```bash
+mkdir build && cd build
+cmake ../
+make  # build tests and examples
+make test  # build and run unit tests
+```
+
+**Note:** Unit tests require Boost. In case it's unavailable you may use `-DHIGHFIVE_USE_BOOST=OFF`.
+HighFive with disable support for Boost types as well as unit tests (though most examples will build).
+
+
+### Contributors
+
+See the list of contributors on [GitHub](https://github.com/BlueBrain/HighFive/graphs/contributors)
+
+Originally created by Adrien Devresse adrien.devresse@epfl.ch - Blue Brain Project
+
+Maintained by the Computing/HPC team, Blue Brain Project - EPFL
+
+
+### License
+
+Boost Software License 1.0
diff --git a/vendor/highfive/VERSION b/vendor/highfive/VERSION
new file mode 100644
index 00000000..b1b25a5f
--- /dev/null
+++ b/vendor/highfive/VERSION
@@ -0,0 +1,1 @@
+2.2.2
diff --git a/vendor/highfive/default.nix b/vendor/highfive/default.nix
new file mode 100644
index 00000000..a692025c
--- /dev/null
+++ b/vendor/highfive/default.nix
@@ -0,0 +1,20 @@
+# Nix development environment
+#
+# build:
+# nix-build -I "BBPpkgs=https://github.com/BlueBrain/bbp-nixpkgs/archive/master.tar.gz" default.nix
+#
+# build and test:
+# nix-build -I "BBPpkgs=https://goo.gl/wTvE5t" --arg testExec true  default.nix  -j 4
+#
+# dev shell:
+# nix-shell -I "BBPpkgs=https://goo.gl/wTvE5t"  default.nix
+#
+with import <BBPpkgs> { };
+
+{
+	highfive = highfive.overrideDerivation (oldAttr: rec {
+      name = "highfive-0.1";
+      src = ./.;
+
+	});
+}
diff --git a/vendor/highfive/doc/CMakeLists.txt b/vendor/highfive/doc/CMakeLists.txt
new file mode 100644
index 00000000..2538af62
--- /dev/null
+++ b/vendor/highfive/doc/CMakeLists.txt
@@ -0,0 +1,7 @@
+find_package(Doxygen)
+if(Doxygen_FOUND)
+    configure_file(${CMAKE_CURRENT_SOURCE_DIR}/Doxyfile ${CMAKE_CURRENT_BINARY_DIR}/Doxyfile @ONLY)
+    add_custom_target(doc COMMAND Doxygen::doxygen ${CMAKE_CURRENT_BINARY_DIR}/Doxyfile)
+else()
+    message(STATUS "  Documentation (doc) cannot be built since Doxygen is not available.")
+endif()
diff --git a/vendor/highfive/doc/Doxyfile b/vendor/highfive/doc/Doxyfile
new file mode 100644
index 00000000..f0d9b4d4
--- /dev/null
+++ b/vendor/highfive/doc/Doxyfile
@@ -0,0 +1,2618 @@
+# Doxyfile 1.9.1
+
+# This file describes the settings to be used by the documentation system
+# doxygen (www.doxygen.org) for a project.
+#
+# All text after a double hash (##) is considered a comment and is placed in
+# front of the TAG it is preceding.
+#
+# All text after a single hash (#) is considered a comment and will be ignored.
+# The format is:
+# TAG = value [value, ...]
+# For lists, items can also be appended using:
+# TAG += value [value, ...]
+# Values that contain spaces should be placed between quotes (\" \").
+
+#---------------------------------------------------------------------------
+# Project related configuration options
+#---------------------------------------------------------------------------
+
+# This tag specifies the encoding used for all characters in the configuration
+# file that follow. The default is UTF-8 which is also the encoding used for all
+# text before the first occurrence of this tag. Doxygen uses libiconv (or the
+# iconv built into libc) for the transcoding. See
+# https://www.gnu.org/software/libiconv/ for the list of possible encodings.
+# The default value is: UTF-8.
+
+DOXYFILE_ENCODING      = UTF-8
+
+# The PROJECT_NAME tag is a single word (or a sequence of words surrounded by
+# double-quotes, unless you are using Doxywizard) that should identify the
+# project for which the documentation is generated. This name is used in the
+# title of most generated pages and in a few other places.
+# The default value is: My Project.
+
+PROJECT_NAME           = HighFive
+
+# The PROJECT_NUMBER tag can be used to enter a project or revision number. This
+# could be handy for archiving the generated documentation or if some version
+# control system is used.
+
+PROJECT_NUMBER         = @PROJECT_VERSION@
+
+# Using the PROJECT_BRIEF tag one can provide an optional one line description
+# for a project that appears at the top of each page and should give viewer a
+# quick idea about the purpose of the project. Keep the description short.
+
+PROJECT_BRIEF          = "HighFive - Header-only C++ HDF5 interface"
+
+# With the PROJECT_LOGO tag one can specify a logo or an icon that is included
+# in the documentation. The maximum height of the logo should not exceed 55
+# pixels and the maximum width should not exceed 200 pixels. Doxygen will copy
+# the logo to the output directory.
+
+PROJECT_LOGO           =
+
+# The OUTPUT_DIRECTORY tag is used to specify the (relative or absolute) path
+# into which the generated documentation will be written. If a relative path is
+# entered, it will be relative to the location where doxygen was started. If
+# left blank the current directory will be used.
+
+OUTPUT_DIRECTORY       =
+
+# If the CREATE_SUBDIRS tag is set to YES then doxygen will create 4096 sub-
+# directories (in 2 levels) under the output directory of each output format and
+# will distribute the generated files over these directories. Enabling this
+# option can be useful when feeding doxygen a huge amount of source files, where
+# putting all generated files in the same directory would otherwise causes
+# performance problems for the file system.
+# The default value is: NO.
+
+CREATE_SUBDIRS         = NO
+
+# If the ALLOW_UNICODE_NAMES tag is set to YES, doxygen will allow non-ASCII
+# characters to appear in the names of generated files. If set to NO, non-ASCII
+# characters will be escaped, for example _xE3_x81_x84 will be used for Unicode
+# U+3044.
+# The default value is: NO.
+
+ALLOW_UNICODE_NAMES    = NO
+
+# The OUTPUT_LANGUAGE tag is used to specify the language in which all
+# documentation generated by doxygen is written. Doxygen will use this
+# information to generate all constant output in the proper language.
+# Possible values are: Afrikaans, Arabic, Armenian, Brazilian, Catalan, Chinese,
+# Chinese-Traditional, Croatian, Czech, Danish, Dutch, English (United States),
+# Esperanto, Farsi (Persian), Finnish, French, German, Greek, Hungarian,
+# Indonesian, Italian, Japanese, Japanese-en (Japanese with English messages),
+# Korean, Korean-en (Korean with English messages), Latvian, Lithuanian,
+# Macedonian, Norwegian, Persian (Farsi), Polish, Portuguese, Romanian, Russian,
+# Serbian, Serbian-Cyrillic, Slovak, Slovene, Spanish, Swedish, Turkish,
+# Ukrainian and Vietnamese.
+# The default value is: English.
+
+OUTPUT_LANGUAGE        = English
+
+# The OUTPUT_TEXT_DIRECTION tag is used to specify the direction in which all
+# documentation generated by doxygen is written. Doxygen will use this
+# information to generate all generated output in the proper direction.
+# Possible values are: None, LTR, RTL and Context.
+# The default value is: None.
+
+OUTPUT_TEXT_DIRECTION  = None
+
+# If the BRIEF_MEMBER_DESC tag is set to YES, doxygen will include brief member
+# descriptions after the members that are listed in the file and class
+# documentation (similar to Javadoc). Set to NO to disable this.
+# The default value is: YES.
+
+BRIEF_MEMBER_DESC      = YES
+
+# If the REPEAT_BRIEF tag is set to YES, doxygen will prepend the brief
+# description of a member or function before the detailed description
+#
+# Note: If both HIDE_UNDOC_MEMBERS and BRIEF_MEMBER_DESC are set to NO, the
+# brief descriptions will be completely suppressed.
+# The default value is: YES.
+
+REPEAT_BRIEF           = YES
+
+# This tag implements a quasi-intelligent brief description abbreviator that is
+# used to form the text in various listings. Each string in this list, if found
+# as the leading text of the brief description, will be stripped from the text
+# and the result, after processing the whole list, is used as the annotated
+# text. Otherwise, the brief description is used as-is. If left blank, the
+# following values are used ($name is automatically replaced with the name of
+# the entity):The $name class, The $name widget, The $name file, is, provides,
+# specifies, contains, represents, a, an and the.
+
+ABBREVIATE_BRIEF       = "The $name class" \
+                         "The $name widget" \
+                         "The $name file" \
+                         is \
+                         provides \
+                         specifies \
+                         contains \
+                         represents \
+                         a \
+                         an \
+                         the
+
+# If the ALWAYS_DETAILED_SEC and REPEAT_BRIEF tags are both set to YES then
+# doxygen will generate a detailed section even if there is only a brief
+# description.
+# The default value is: NO.
+
+ALWAYS_DETAILED_SEC    = NO
+
+# If the INLINE_INHERITED_MEMB tag is set to YES, doxygen will show all
+# inherited members of a class in the documentation of that class as if those
+# members were ordinary class members. Constructors, destructors and assignment
+# operators of the base classes will not be shown.
+# The default value is: NO.
+
+INLINE_INHERITED_MEMB  = NO
+
+# If the FULL_PATH_NAMES tag is set to YES, doxygen will prepend the full path
+# before files name in the file list and in the header files. If set to NO the
+# shortest path that makes the file name unique will be used
+# The default value is: YES.
+
+FULL_PATH_NAMES        = YES
+
+# The STRIP_FROM_PATH tag can be used to strip a user-defined part of the path.
+# Stripping is only done if one of the specified strings matches the left-hand
+# part of the path. The tag can be used to show relative paths in the file list.
+# If left blank the directory from which doxygen is run is used as the path to
+# strip.
+#
+# Note that you can specify absolute paths here, but also relative paths, which
+# will be relative from the directory where doxygen is started.
+# This tag requires that the tag FULL_PATH_NAMES is set to YES.
+
+STRIP_FROM_PATH        =
+
+# The STRIP_FROM_INC_PATH tag can be used to strip a user-defined part of the
+# path mentioned in the documentation of a class, which tells the reader which
+# header file to include in order to use a class. If left blank only the name of
+# the header file containing the class definition is used. Otherwise one should
+# specify the list of include paths that are normally passed to the compiler
+# using the -I flag.
+
+STRIP_FROM_INC_PATH    =
+
+# If the SHORT_NAMES tag is set to YES, doxygen will generate much shorter (but
+# less readable) file names. This can be useful is your file systems doesn't
+# support long names like on DOS, Mac, or CD-ROM.
+# The default value is: NO.
+
+SHORT_NAMES            = NO
+
+# If the JAVADOC_AUTOBRIEF tag is set to YES then doxygen will interpret the
+# first line (until the first dot) of a Javadoc-style comment as the brief
+# description. If set to NO, the Javadoc-style will behave just like regular Qt-
+# style comments (thus requiring an explicit @brief command for a brief
+# description.)
+# The default value is: NO.
+
+JAVADOC_AUTOBRIEF      = NO
+
+# If the JAVADOC_BANNER tag is set to YES then doxygen will interpret a line
+# such as
+# /***************
+# as being the beginning of a Javadoc-style comment "banner". If set to NO, the
+# Javadoc-style will behave just like regular comments and it will not be
+# interpreted by doxygen.
+# The default value is: NO.
+
+JAVADOC_BANNER         = NO
+
+# If the QT_AUTOBRIEF tag is set to YES then doxygen will interpret the first
+# line (until the first dot) of a Qt-style comment as the brief description. If
+# set to NO, the Qt-style will behave just like regular Qt-style comments (thus
+# requiring an explicit \brief command for a brief description.)
+# The default value is: NO.
+
+QT_AUTOBRIEF           = NO
+
+# The MULTILINE_CPP_IS_BRIEF tag can be set to YES to make doxygen treat a
+# multi-line C++ special comment block (i.e. a block of //! or /// comments) as
+# a brief description. This used to be the default behavior. The new default is
+# to treat a multi-line C++ comment block as a detailed description. Set this
+# tag to YES if you prefer the old behavior instead.
+#
+# Note that setting this tag to YES also means that rational rose comments are
+# not recognized any more.
+# The default value is: NO.
+
+MULTILINE_CPP_IS_BRIEF = NO
+
+# By default Python docstrings are displayed as preformatted text and doxygen's
+# special commands cannot be used. By setting PYTHON_DOCSTRING to NO the
+# doxygen's special commands can be used and the contents of the docstring
+# documentation blocks is shown as doxygen documentation.
+# The default value is: YES.
+
+PYTHON_DOCSTRING       = YES
+
+# If the INHERIT_DOCS tag is set to YES then an undocumented member inherits the
+# documentation from any documented member that it re-implements.
+# The default value is: YES.
+
+INHERIT_DOCS           = YES
+
+# If the SEPARATE_MEMBER_PAGES tag is set to YES then doxygen will produce a new
+# page for each member. If set to NO, the documentation of a member will be part
+# of the file/class/namespace that contains it.
+# The default value is: NO.
+
+SEPARATE_MEMBER_PAGES  = NO
+
+# The TAB_SIZE tag can be used to set the number of spaces in a tab. Doxygen
+# uses this value to replace tabs by spaces in code fragments.
+# Minimum value: 1, maximum value: 16, default value: 4.
+
+TAB_SIZE               = 4
+
+# This tag can be used to specify a number of aliases that act as commands in
+# the documentation. An alias has the form:
+# name=value
+# For example adding
+# "sideeffect=@par Side Effects:\n"
+# will allow you to put the command \sideeffect (or @sideeffect) in the
+# documentation, which will result in a user-defined paragraph with heading
+# "Side Effects:". You can put \n's in the value part of an alias to insert
+# newlines (in the resulting output). You can put ^^ in the value part of an
+# alias to insert a newline as if a physical newline was in the original file.
+# When you need a literal { or } or , in the value part of an alias you have to
+# escape them by means of a backslash (\), this can lead to conflicts with the
+# commands \{ and \} for these it is advised to use the version @{ and @} or use
+# a double escape (\\{ and \\})
+
+ALIASES                =
+
+# Set the OPTIMIZE_OUTPUT_FOR_C tag to YES if your project consists of C sources
+# only. Doxygen will then generate output that is more tailored for C. For
+# instance, some of the names that are used will be different. The list of all
+# members will be omitted, etc.
+# The default value is: NO.
+
+OPTIMIZE_OUTPUT_FOR_C  = NO
+
+# Set the OPTIMIZE_OUTPUT_JAVA tag to YES if your project consists of Java or
+# Python sources only. Doxygen will then generate output that is more tailored
+# for that language. For instance, namespaces will be presented as packages,
+# qualified scopes will look different, etc.
+# The default value is: NO.
+
+OPTIMIZE_OUTPUT_JAVA   = NO
+
+# Set the OPTIMIZE_FOR_FORTRAN tag to YES if your project consists of Fortran
+# sources. Doxygen will then generate output that is tailored for Fortran.
+# The default value is: NO.
+
+OPTIMIZE_FOR_FORTRAN   = NO
+
+# Set the OPTIMIZE_OUTPUT_VHDL tag to YES if your project consists of VHDL
+# sources. Doxygen will then generate output that is tailored for VHDL.
+# The default value is: NO.
+
+OPTIMIZE_OUTPUT_VHDL   = NO
+
+# Set the OPTIMIZE_OUTPUT_SLICE tag to YES if your project consists of Slice
+# sources only. Doxygen will then generate output that is more tailored for that
+# language. For instance, namespaces will be presented as modules, types will be
+# separated into more groups, etc.
+# The default value is: NO.
+
+OPTIMIZE_OUTPUT_SLICE  = NO
+
+# Doxygen selects the parser to use depending on the extension of the files it
+# parses. With this tag you can assign which parser to use for a given
+# extension. Doxygen has a built-in mapping, but you can override or extend it
+# using this tag. The format is ext=language, where ext is a file extension, and
+# language is one of the parsers supported by doxygen: IDL, Java, JavaScript,
+# Csharp (C#), C, C++, D, PHP, md (Markdown), Objective-C, Python, Slice, VHDL,
+# Fortran (fixed format Fortran: FortranFixed, free formatted Fortran:
+# FortranFree, unknown formatted Fortran: Fortran. In the later case the parser
+# tries to guess whether the code is fixed or free formatted code, this is the
+# default for Fortran type files). For instance to make doxygen treat .inc files
+# as Fortran files (default is PHP), and .f files as C (default is Fortran),
+# use: inc=Fortran f=C.
+#
+# Note: For files without extension you can use no_extension as a placeholder.
+#
+# Note that for custom extensions you also need to set FILE_PATTERNS otherwise
+# the files are not read by doxygen. When specifying no_extension you should add
+# * to the FILE_PATTERNS.
+#
+# Note see also the list of default file extension mappings.
+
+EXTENSION_MAPPING      =
+
+# If the MARKDOWN_SUPPORT tag is enabled then doxygen pre-processes all comments
+# according to the Markdown format, which allows for more readable
+# documentation. See https://daringfireball.net/projects/markdown/ for details.
+# The output of markdown processing is further processed by doxygen, so you can
+# mix doxygen, HTML, and XML commands with Markdown formatting. Disable only in
+# case of backward compatibilities issues.
+# The default value is: YES.
+
+MARKDOWN_SUPPORT       = YES
+
+# When the TOC_INCLUDE_HEADINGS tag is set to a non-zero value, all headings up
+# to that level are automatically included in the table of contents, even if
+# they do not have an id attribute.
+# Note: This feature currently applies only to Markdown headings.
+# Minimum value: 0, maximum value: 99, default value: 5.
+# This tag requires that the tag MARKDOWN_SUPPORT is set to YES.
+
+TOC_INCLUDE_HEADINGS   = 0
+
+# When enabled doxygen tries to link words that correspond to documented
+# classes, or namespaces to their corresponding documentation. Such a link can
+# be prevented in individual cases by putting a % sign in front of the word or
+# globally by setting AUTOLINK_SUPPORT to NO.
+# The default value is: YES.
+
+AUTOLINK_SUPPORT       = YES
+
+# If you use STL classes (i.e. std::string, std::vector, etc.) but do not want
+# to include (a tag file for) the STL sources as input, then you should set this
+# tag to YES in order to let doxygen match functions declarations and
+# definitions whose arguments contain STL classes (e.g. func(std::string);
+# versus func(std::string) {}). This also make the inheritance and collaboration
+# diagrams that involve STL classes more complete and accurate.
+# The default value is: NO.
+
+BUILTIN_STL_SUPPORT    = NO
+
+# If you use Microsoft's C++/CLI language, you should set this option to YES to
+# enable parsing support.
+# The default value is: NO.
+
+CPP_CLI_SUPPORT        = NO
+
+# Set the SIP_SUPPORT tag to YES if your project consists of sip (see:
+# https://www.riverbankcomputing.com/software/sip/intro) sources only. Doxygen
+# will parse them like normal C++ but will assume all classes use public instead
+# of private inheritance when no explicit protection keyword is present.
+# The default value is: NO.
+
+SIP_SUPPORT            = NO
+
+# For Microsoft's IDL there are propget and propput attributes to indicate
+# getter and setter methods for a property. Setting this option to YES will make
+# doxygen to replace the get and set methods by a property in the documentation.
+# This will only work if the methods are indeed getting or setting a simple
+# type. If this is not the case, or you want to show the methods anyway, you
+# should set this option to NO.
+# The default value is: YES.
+
+IDL_PROPERTY_SUPPORT   = YES
+
+# If member grouping is used in the documentation and the DISTRIBUTE_GROUP_DOC
+# tag is set to YES then doxygen will reuse the documentation of the first
+# member in the group (if any) for the other members of the group. By default
+# all members of a group must be documented explicitly.
+# The default value is: NO.
+
+DISTRIBUTE_GROUP_DOC   = NO
+
+# If one adds a struct or class to a group and this option is enabled, then also
+# any nested class or struct is added to the same group. By default this option
+# is disabled and one has to add nested compounds explicitly via \ingroup.
+# The default value is: NO.
+
+GROUP_NESTED_COMPOUNDS = NO
+
+# Set the SUBGROUPING tag to YES to allow class member groups of the same type
+# (for instance a group of public functions) to be put as a subgroup of that
+# type (e.g. under the Public Functions section). Set it to NO to prevent
+# subgrouping. Alternatively, this can be done per class using the
+# \nosubgrouping command.
+# The default value is: YES.
+
+SUBGROUPING            = YES
+
+# When the INLINE_GROUPED_CLASSES tag is set to YES, classes, structs and unions
+# are shown inside the group in which they are included (e.g. using \ingroup)
+# instead of on a separate page (for HTML and Man pages) or section (for LaTeX
+# and RTF).
+#
+# Note that this feature does not work in combination with
+# SEPARATE_MEMBER_PAGES.
+# The default value is: NO.
+
+INLINE_GROUPED_CLASSES = NO
+
+# When the INLINE_SIMPLE_STRUCTS tag is set to YES, structs, classes, and unions
+# with only public data fields or simple typedef fields will be shown inline in
+# the documentation of the scope in which they are defined (i.e. file,
+# namespace, or group documentation), provided this scope is documented. If set
+# to NO, structs, classes, and unions are shown on a separate page (for HTML and
+# Man pages) or section (for LaTeX and RTF).
+# The default value is: NO.
+
+INLINE_SIMPLE_STRUCTS  = NO
+
+# When TYPEDEF_HIDES_STRUCT tag is enabled, a typedef of a struct, union, or
+# enum is documented as struct, union, or enum with the name of the typedef. So
+# typedef struct TypeS {} TypeT, will appear in the documentation as a struct
+# with name TypeT. When disabled the typedef will appear as a member of a file,
+# namespace, or class. And the struct will be named TypeS. This can typically be
+# useful for C code in case the coding convention dictates that all compound
+# types are typedef'ed and only the typedef is referenced, never the tag name.
+# The default value is: NO.
+
+TYPEDEF_HIDES_STRUCT   = NO
+
+# The size of the symbol lookup cache can be set using LOOKUP_CACHE_SIZE. This
+# cache is used to resolve symbols given their name and scope. Since this can be
+# an expensive process and often the same symbol appears multiple times in the
+# code, doxygen keeps a cache of pre-resolved symbols. If the cache is too small
+# doxygen will become slower. If the cache is too large, memory is wasted. The
+# cache size is given by this formula: 2^(16+LOOKUP_CACHE_SIZE). The valid range
+# is 0..9, the default is 0, corresponding to a cache size of 2^16=65536
+# symbols. At the end of a run doxygen will report the cache usage and suggest
+# the optimal cache size from a speed point of view.
+# Minimum value: 0, maximum value: 9, default value: 0.
+
+LOOKUP_CACHE_SIZE      = 0
+
+# The NUM_PROC_THREADS specifies the number threads doxygen is allowed to use
+# during processing. When set to 0 doxygen will based this on the number of
+# cores available in the system. You can set it explicitly to a value larger
+# than 0 to get more control over the balance between CPU load and processing
+# speed. At this moment only the input processing can be done using multiple
+# threads. Since this is still an experimental feature the default is set to 1,
+# which efficively disables parallel processing. Please report any issues you
+# encounter. Generating dot graphs in parallel is controlled by the
+# DOT_NUM_THREADS setting.
+# Minimum value: 0, maximum value: 32, default value: 1.
+
+NUM_PROC_THREADS       = 1
+
+#---------------------------------------------------------------------------
+# Build related configuration options
+#---------------------------------------------------------------------------
+
+# If the EXTRACT_ALL tag is set to YES, doxygen will assume all entities in
+# documentation are documented, even if no documentation was available. Private
+# class members and static file members will be hidden unless the
+# EXTRACT_PRIVATE respectively EXTRACT_STATIC tags are set to YES.
+# Note: This will also disable the warnings about undocumented members that are
+# normally produced when WARNINGS is set to YES.
+# The default value is: NO.
+
+EXTRACT_ALL            = YES
+
+# If the EXTRACT_PRIVATE tag is set to YES, all private members of a class will
+# be included in the documentation.
+# The default value is: NO.
+
+EXTRACT_PRIVATE        = NO
+
+# If the EXTRACT_PRIV_VIRTUAL tag is set to YES, documented private virtual
+# methods of a class will be included in the documentation.
+# The default value is: NO.
+
+EXTRACT_PRIV_VIRTUAL   = NO
+
+# If the EXTRACT_PACKAGE tag is set to YES, all members with package or internal
+# scope will be included in the documentation.
+# The default value is: NO.
+
+EXTRACT_PACKAGE        = NO
+
+# If the EXTRACT_STATIC tag is set to YES, all static members of a file will be
+# included in the documentation.
+# The default value is: NO.
+
+EXTRACT_STATIC         = NO
+
+# If the EXTRACT_LOCAL_CLASSES tag is set to YES, classes (and structs) defined
+# locally in source files will be included in the documentation. If set to NO,
+# only classes defined in header files are included. Does not have any effect
+# for Java sources.
+# The default value is: YES.
+
+EXTRACT_LOCAL_CLASSES  = YES
+
+# This flag is only useful for Objective-C code. If set to YES, local methods,
+# which are defined in the implementation section but not in the interface are
+# included in the documentation. If set to NO, only methods in the interface are
+# included.
+# The default value is: NO.
+
+EXTRACT_LOCAL_METHODS  = NO
+
+# If this flag is set to YES, the members of anonymous namespaces will be
+# extracted and appear in the documentation as a namespace called
+# 'anonymous_namespace{file}', where file will be replaced with the base name of
+# the file that contains the anonymous namespace. By default anonymous namespace
+# are hidden.
+# The default value is: NO.
+
+EXTRACT_ANON_NSPACES   = NO
+
+# If this flag is set to YES, the name of an unnamed parameter in a declaration
+# will be determined by the corresponding definition. By default unnamed
+# parameters remain unnamed in the output.
+# The default value is: YES.
+
+RESOLVE_UNNAMED_PARAMS = YES
+
+# If the HIDE_UNDOC_MEMBERS tag is set to YES, doxygen will hide all
+# undocumented members inside documented classes or files. If set to NO these
+# members will be included in the various overviews, but no documentation
+# section is generated. This option has no effect if EXTRACT_ALL is enabled.
+# The default value is: NO.
+
+HIDE_UNDOC_MEMBERS     = NO
+
+# If the HIDE_UNDOC_CLASSES tag is set to YES, doxygen will hide all
+# undocumented classes that are normally visible in the class hierarchy. If set
+# to NO, these classes will be included in the various overviews. This option
+# has no effect if EXTRACT_ALL is enabled.
+# The default value is: NO.
+
+HIDE_UNDOC_CLASSES     = NO
+
+# If the HIDE_FRIEND_COMPOUNDS tag is set to YES, doxygen will hide all friend
+# declarations. If set to NO, these declarations will be included in the
+# documentation.
+# The default value is: NO.
+
+HIDE_FRIEND_COMPOUNDS  = NO
+
+# If the HIDE_IN_BODY_DOCS tag is set to YES, doxygen will hide any
+# documentation blocks found inside the body of a function. If set to NO, these
+# blocks will be appended to the function's detailed documentation block.
+# The default value is: NO.
+
+HIDE_IN_BODY_DOCS      = NO
+
+# The INTERNAL_DOCS tag determines if documentation that is typed after a
+# \internal command is included. If the tag is set to NO then the documentation
+# will be excluded. Set it to YES to include the internal documentation.
+# The default value is: NO.
+
+INTERNAL_DOCS          = NO
+
+# With the correct setting of option CASE_SENSE_NAMES doxygen will better be
+# able to match the capabilities of the underlying filesystem. In case the
+# filesystem is case sensitive (i.e. it supports files in the same directory
+# whose names only differ in casing), the option must be set to YES to properly
+# deal with such files in case they appear in the input. For filesystems that
+# are not case sensitive the option should be be set to NO to properly deal with
+# output files written for symbols that only differ in casing, such as for two
+# classes, one named CLASS and the other named Class, and to also support
+# references to files without having to specify the exact matching casing. On
+# Windows (including Cygwin) and MacOS, users should typically set this option
+# to NO, whereas on Linux or other Unix flavors it should typically be set to
+# YES.
+# The default value is: system dependent.
+
+CASE_SENSE_NAMES       = NO
+
+# If the HIDE_SCOPE_NAMES tag is set to NO then doxygen will show members with
+# their full class and namespace scopes in the documentation. If set to YES, the
+# scope will be hidden.
+# The default value is: NO.
+
+HIDE_SCOPE_NAMES       = NO
+
+# If the HIDE_COMPOUND_REFERENCE tag is set to NO (default) then doxygen will
+# append additional text to a page's title, such as Class Reference. If set to
+# YES the compound reference will be hidden.
+# The default value is: NO.
+
+HIDE_COMPOUND_REFERENCE= NO
+
+# If the SHOW_INCLUDE_FILES tag is set to YES then doxygen will put a list of
+# the files that are included by a file in the documentation of that file.
+# The default value is: YES.
+
+SHOW_INCLUDE_FILES     = YES
+
+# If the SHOW_GROUPED_MEMB_INC tag is set to YES then Doxygen will add for each
+# grouped member an include statement to the documentation, telling the reader
+# which file to include in order to use the member.
+# The default value is: NO.
+
+SHOW_GROUPED_MEMB_INC  = NO
+
+# If the FORCE_LOCAL_INCLUDES tag is set to YES then doxygen will list include
+# files with double quotes in the documentation rather than with sharp brackets.
+# The default value is: NO.
+
+FORCE_LOCAL_INCLUDES   = NO
+
+# If the INLINE_INFO tag is set to YES then a tag [inline] is inserted in the
+# documentation for inline members.
+# The default value is: YES.
+
+INLINE_INFO            = YES
+
+# If the SORT_MEMBER_DOCS tag is set to YES then doxygen will sort the
+# (detailed) documentation of file and class members alphabetically by member
+# name. If set to NO, the members will appear in declaration order.
+# The default value is: YES.
+
+SORT_MEMBER_DOCS       = YES
+
+# If the SORT_BRIEF_DOCS tag is set to YES then doxygen will sort the brief
+# descriptions of file, namespace and class members alphabetically by member
+# name. If set to NO, the members will appear in declaration order. Note that
+# this will also influence the order of the classes in the class list.
+# The default value is: NO.
+
+SORT_BRIEF_DOCS        = NO
+
+# If the SORT_MEMBERS_CTORS_1ST tag is set to YES then doxygen will sort the
+# (brief and detailed) documentation of class members so that constructors and
+# destructors are listed first. If set to NO the constructors will appear in the
+# respective orders defined by SORT_BRIEF_DOCS and SORT_MEMBER_DOCS.
+# Note: If SORT_BRIEF_DOCS is set to NO this option is ignored for sorting brief
+# member documentation.
+# Note: If SORT_MEMBER_DOCS is set to NO this option is ignored for sorting
+# detailed member documentation.
+# The default value is: NO.
+
+SORT_MEMBERS_CTORS_1ST = NO
+
+# If the SORT_GROUP_NAMES tag is set to YES then doxygen will sort the hierarchy
+# of group names into alphabetical order. If set to NO the group names will
+# appear in their defined order.
+# The default value is: NO.
+
+SORT_GROUP_NAMES       = NO
+
+# If the SORT_BY_SCOPE_NAME tag is set to YES, the class list will be sorted by
+# fully-qualified names, including namespaces. If set to NO, the class list will
+# be sorted only by class name, not including the namespace part.
+# Note: This option is not very useful if HIDE_SCOPE_NAMES is set to YES.
+# Note: This option applies only to the class list, not to the alphabetical
+# list.
+# The default value is: NO.
+
+SORT_BY_SCOPE_NAME     = NO
+
+# If the STRICT_PROTO_MATCHING option is enabled and doxygen fails to do proper
+# type resolution of all parameters of a function it will reject a match between
+# the prototype and the implementation of a member function even if there is
+# only one candidate or it is obvious which candidate to choose by doing a
+# simple string match. By disabling STRICT_PROTO_MATCHING doxygen will still
+# accept a match between prototype and implementation in such cases.
+# The default value is: NO.
+
+STRICT_PROTO_MATCHING  = NO
+
+# The GENERATE_TODOLIST tag can be used to enable (YES) or disable (NO) the todo
+# list. This list is created by putting \todo commands in the documentation.
+# The default value is: YES.
+
+GENERATE_TODOLIST      = YES
+
+# The GENERATE_TESTLIST tag can be used to enable (YES) or disable (NO) the test
+# list. This list is created by putting \test commands in the documentation.
+# The default value is: YES.
+
+GENERATE_TESTLIST      = YES
+
+# The GENERATE_BUGLIST tag can be used to enable (YES) or disable (NO) the bug
+# list. This list is created by putting \bug commands in the documentation.
+# The default value is: YES.
+
+GENERATE_BUGLIST       = YES
+
+# The GENERATE_DEPRECATEDLIST tag can be used to enable (YES) or disable (NO)
+# the deprecated list. This list is created by putting \deprecated commands in
+# the documentation.
+# The default value is: YES.
+
+GENERATE_DEPRECATEDLIST= YES
+
+# The ENABLED_SECTIONS tag can be used to enable conditional documentation
+# sections, marked by \if <section_label> ... \endif and \cond <section_label>
+# ... \endcond blocks.
+
+ENABLED_SECTIONS       =
+
+# The MAX_INITIALIZER_LINES tag determines the maximum number of lines that the
+# initial value of a variable or macro / define can have for it to appear in the
+# documentation. If the initializer consists of more lines than specified here
+# it will be hidden. Use a value of 0 to hide initializers completely. The
+# appearance of the value of individual variables and macros / defines can be
+# controlled using \showinitializer or \hideinitializer command in the
+# documentation regardless of this setting.
+# Minimum value: 0, maximum value: 10000, default value: 30.
+
+MAX_INITIALIZER_LINES  = 30
+
+# Set the SHOW_USED_FILES tag to NO to disable the list of files generated at
+# the bottom of the documentation of classes and structs. If set to YES, the
+# list will mention the files that were used to generate the documentation.
+# The default value is: YES.
+
+SHOW_USED_FILES        = YES
+
+# Set the SHOW_FILES tag to NO to disable the generation of the Files page. This
+# will remove the Files entry from the Quick Index and from the Folder Tree View
+# (if specified).
+# The default value is: YES.
+
+SHOW_FILES             = YES
+
+# Set the SHOW_NAMESPACES tag to NO to disable the generation of the Namespaces
+# page. This will remove the Namespaces entry from the Quick Index and from the
+# Folder Tree View (if specified).
+# The default value is: YES.
+
+SHOW_NAMESPACES        = YES
+
+# The FILE_VERSION_FILTER tag can be used to specify a program or script that
+# doxygen should invoke to get the current version for each file (typically from
+# the version control system). Doxygen will invoke the program by executing (via
+# popen()) the command command input-file, where command is the value of the
+# FILE_VERSION_FILTER tag, and input-file is the name of an input file provided
+# by doxygen. Whatever the program writes to standard output is used as the file
+# version. For an example see the documentation.
+
+FILE_VERSION_FILTER    =
+
+# The LAYOUT_FILE tag can be used to specify a layout file which will be parsed
+# by doxygen. The layout file controls the global structure of the generated
+# output files in an output format independent way. To create the layout file
+# that represents doxygen's defaults, run doxygen with the -l option. You can
+# optionally specify a file name after the option, if omitted DoxygenLayout.xml
+# will be used as the name of the layout file.
+#
+# Note that if you run doxygen from a directory containing a file called
+# DoxygenLayout.xml, doxygen will parse it automatically even if the LAYOUT_FILE
+# tag is left empty.
+
+LAYOUT_FILE            =
+
+# The CITE_BIB_FILES tag can be used to specify one or more bib files containing
+# the reference definitions. This must be a list of .bib files. The .bib
+# extension is automatically appended if omitted. This requires the bibtex tool
+# to be installed. See also https://en.wikipedia.org/wiki/BibTeX for more info.
+# For LaTeX the style of the bibliography can be controlled using
+# LATEX_BIB_STYLE. To use this feature you need bibtex and perl available in the
+# search path. See also \cite for info how to create references.
+
+CITE_BIB_FILES         =
+
+#---------------------------------------------------------------------------
+# Configuration options related to warning and progress messages
+#---------------------------------------------------------------------------
+
+# The QUIET tag can be used to turn on/off the messages that are generated to
+# standard output by doxygen. If QUIET is set to YES this implies that the
+# messages are off.
+# The default value is: NO.
+
+QUIET                  = YES
+
+# The WARNINGS tag can be used to turn on/off the warning messages that are
+# generated to standard error (stderr) by doxygen. If WARNINGS is set to YES
+# this implies that the warnings are on.
+#
+# Tip: Turn warnings on while writing the documentation.
+# The default value is: YES.
+
+WARNINGS               = YES
+
+# If the WARN_IF_UNDOCUMENTED tag is set to YES then doxygen will generate
+# warnings for undocumented members. If EXTRACT_ALL is set to YES then this flag
+# will automatically be disabled.
+# The default value is: YES.
+
+WARN_IF_UNDOCUMENTED   = YES
+
+# If the WARN_IF_DOC_ERROR tag is set to YES, doxygen will generate warnings for
+# potential errors in the documentation, such as not documenting some parameters
+# in a documented function, or documenting parameters that don't exist or using
+# markup commands wrongly.
+# The default value is: YES.
+
+WARN_IF_DOC_ERROR      = YES
+
+# This WARN_NO_PARAMDOC option can be enabled to get warnings for functions that
+# are documented, but have no documentation for their parameters or return
+# value. If set to NO, doxygen will only warn about wrong or incomplete
+# parameter documentation, but not about the absence of documentation. If
+# EXTRACT_ALL is set to YES then this flag will automatically be disabled.
+# The default value is: NO.
+
+WARN_NO_PARAMDOC       = NO
+
+# If the WARN_AS_ERROR tag is set to YES then doxygen will immediately stop when
+# a warning is encountered. If the WARN_AS_ERROR tag is set to FAIL_ON_WARNINGS
+# then doxygen will continue running as if WARN_AS_ERROR tag is set to NO, but
+# at the end of the doxygen process doxygen will return with a non-zero status.
+# Possible values are: NO, YES and FAIL_ON_WARNINGS.
+# The default value is: NO.
+
+WARN_AS_ERROR          = NO
+
+# The WARN_FORMAT tag determines the format of the warning messages that doxygen
+# can produce. The string should contain the $file, $line, and $text tags, which
+# will be replaced by the file and line number from which the warning originated
+# and the warning text. Optionally the format may contain $version, which will
+# be replaced by the version of the file (if it could be obtained via
+# FILE_VERSION_FILTER)
+# The default value is: $file:$line: $text.
+
+WARN_FORMAT            = "$file:$line: $text"
+
+# The WARN_LOGFILE tag can be used to specify a file to which warning and error
+# messages should be written. If left blank the output is written to standard
+# error (stderr).
+
+WARN_LOGFILE           =
+
+#---------------------------------------------------------------------------
+# Configuration options related to the input files
+#---------------------------------------------------------------------------
+
+# The INPUT tag is used to specify the files and/or directories that contain
+# documented source files. You may enter file names like myfile.cpp or
+# directories like /usr/src/myproject. Separate the files or directories with
+# spaces. See also FILE_PATTERNS and EXTENSION_MAPPING
+# Note: If this tag is empty the current directory is searched.
+
+INPUT                  = @CMAKE_CURRENT_SOURCE_DIR@/../include \
+                         @CMAKE_CURRENT_SOURCE_DIR@/mainpage.md
+
+# This tag can be used to specify the character encoding of the source files
+# that doxygen parses. Internally doxygen uses the UTF-8 encoding. Doxygen uses
+# libiconv (or the iconv built into libc) for the transcoding. See the libiconv
+# documentation (see:
+# https://www.gnu.org/software/libiconv/) for the list of possible encodings.
+# The default value is: UTF-8.
+
+INPUT_ENCODING         = UTF-8
+
+# If the value of the INPUT tag contains directories, you can use the
+# FILE_PATTERNS tag to specify one or more wildcard patterns (like *.cpp and
+# *.h) to filter out the source-files in the directories.
+#
+# Note that for custom extensions or not directly supported extensions you also
+# need to set EXTENSION_MAPPING for the extension otherwise the files are not
+# read by doxygen.
+#
+# Note the list of default checked file patterns might differ from the list of
+# default file extension mappings.
+#
+# If left blank the following patterns are tested:*.c, *.cc, *.cxx, *.cpp,
+# *.c++, *.java, *.ii, *.ixx, *.ipp, *.i++, *.inl, *.idl, *.ddl, *.odl, *.h,
+# *.hh, *.hxx, *.hpp, *.h++, *.cs, *.d, *.php, *.php4, *.php5, *.phtml, *.inc,
+# *.m, *.markdown, *.md, *.mm, *.dox (to be provided as doxygen C comment),
+# *.py, *.pyw, *.f90, *.f95, *.f03, *.f08, *.f18, *.f, *.for, *.vhd, *.vhdl,
+# *.ucf, *.qsf and *.ice.
+
+FILE_PATTERNS          = *.c \
+                         *.cc \
+                         *.cxx \
+                         *.cpp \
+                         *.c++ \
+                         *.java \
+                         *.ii \
+                         *.ixx \
+                         *.ipp \
+                         *.i++ \
+                         *.inl \
+                         *.idl \
+                         *.ddl \
+                         *.odl \
+                         *.h \
+                         *.hh \
+                         *.hxx \
+                         *.hpp \
+                         *.h++ \
+                         *.cs \
+                         *.d \
+                         *.php \
+                         *.php4 \
+                         *.php5 \
+                         *.phtml \
+                         *.inc \
+                         *.m \
+                         *.markdown \
+                         *.md \
+                         *.mm \
+                         *.dox \
+                         *.py \
+                         *.pyw \
+                         *.f90 \
+                         *.f95 \
+                         *.f03 \
+                         *.f08 \
+                         *.f \
+                         *.for \
+                         *.tcl \
+                         *.vhd \
+                         *.vhdl \
+                         *.ucf \
+                         *.qsf
+
+# The RECURSIVE tag can be used to specify whether or not subdirectories should
+# be searched for input files as well.
+# The default value is: NO.
+
+RECURSIVE              = YES
+
+# The EXCLUDE tag can be used to specify files and/or directories that should be
+# excluded from the INPUT source files. This way you can easily exclude a
+# subdirectory from a directory tree whose root is specified with the INPUT tag.
+#
+# Note that relative paths are relative to the directory from which doxygen is
+# run.
+
+EXCLUDE                =
+
+# The EXCLUDE_SYMLINKS tag can be used to select whether or not files or
+# directories that are symbolic links (a Unix file system feature) are excluded
+# from the input.
+# The default value is: NO.
+
+EXCLUDE_SYMLINKS       = NO
+
+# If the value of the INPUT tag contains directories, you can use the
+# EXCLUDE_PATTERNS tag to specify one or more wildcard patterns to exclude
+# certain files from those directories.
+#
+# Note that the wildcards are matched against the file with absolute path, so to
+# exclude all test directories for example use the pattern */test/*
+
+EXCLUDE_PATTERNS       =
+
+# The EXCLUDE_SYMBOLS tag can be used to specify one or more symbol names
+# (namespaces, classes, functions, etc.) that should be excluded from the
+# output. The symbol name can be a fully qualified name, a word, or if the
+# wildcard * is used, a substring. Examples: ANamespace, AClass,
+# AClass::ANamespace, ANamespace::*Test
+#
+# Note that the wildcards are matched against the file with absolute path, so to
+# exclude all test directories use the pattern */test/*
+
+EXCLUDE_SYMBOLS        = *detail*
+
+# The EXAMPLE_PATH tag can be used to specify one or more files or directories
+# that contain example code fragments that are included (see the \include
+# command).
+
+EXAMPLE_PATH           =
+
+# If the value of the EXAMPLE_PATH tag contains directories, you can use the
+# EXAMPLE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp and
+# *.h) to filter out the source-files in the directories. If left blank all
+# files are included.
+
+EXAMPLE_PATTERNS       = *
+
+# If the EXAMPLE_RECURSIVE tag is set to YES then subdirectories will be
+# searched for input files to be used with the \include or \dontinclude commands
+# irrespective of the value of the RECURSIVE tag.
+# The default value is: NO.
+
+EXAMPLE_RECURSIVE      = NO
+
+# The IMAGE_PATH tag can be used to specify one or more files or directories
+# that contain images that are to be included in the documentation (see the
+# \image command).
+
+IMAGE_PATH             =
+
+# The INPUT_FILTER tag can be used to specify a program that doxygen should
+# invoke to filter for each input file. Doxygen will invoke the filter program
+# by executing (via popen()) the command:
+#
+# <filter> <input-file>
+#
+# where <filter> is the value of the INPUT_FILTER tag, and <input-file> is the
+# name of an input file. Doxygen will then use the output that the filter
+# program writes to standard output. If FILTER_PATTERNS is specified, this tag
+# will be ignored.
+#
+# Note that the filter must not add or remove lines; it is applied before the
+# code is scanned, but not when the output code is generated. If lines are added
+# or removed, the anchors will not be placed correctly.
+#
+# Note that for custom extensions or not directly supported extensions you also
+# need to set EXTENSION_MAPPING for the extension otherwise the files are not
+# properly processed by doxygen.
+
+INPUT_FILTER           =
+
+# The FILTER_PATTERNS tag can be used to specify filters on a per file pattern
+# basis. Doxygen will compare the file name with each pattern and apply the
+# filter if there is a match. The filters are a list of the form: pattern=filter
+# (like *.cpp=my_cpp_filter). See INPUT_FILTER for further information on how
+# filters are used. If the FILTER_PATTERNS tag is empty or if none of the
+# patterns match the file name, INPUT_FILTER is applied.
+#
+# Note that for custom extensions or not directly supported extensions you also
+# need to set EXTENSION_MAPPING for the extension otherwise the files are not
+# properly processed by doxygen.
+
+FILTER_PATTERNS        =
+
+# If the FILTER_SOURCE_FILES tag is set to YES, the input filter (if set using
+# INPUT_FILTER) will also be used to filter the input files that are used for
+# producing the source files to browse (i.e. when SOURCE_BROWSER is set to YES).
+# The default value is: NO.
+
+FILTER_SOURCE_FILES    = NO
+
+# The FILTER_SOURCE_PATTERNS tag can be used to specify source filters per file
+# pattern. A pattern will override the setting for FILTER_PATTERN (if any) and
+# it is also possible to disable source filtering for a specific pattern using
+# *.ext= (so without naming a filter).
+# This tag requires that the tag FILTER_SOURCE_FILES is set to YES.
+
+FILTER_SOURCE_PATTERNS =
+
+# If the USE_MDFILE_AS_MAINPAGE tag refers to the name of a markdown file that
+# is part of the input, its contents will be placed on the main page
+# (index.html). This can be useful if you have a project on for instance GitHub
+# and want to reuse the introduction page also for the doxygen output.
+
+USE_MDFILE_AS_MAINPAGE =
+
+#---------------------------------------------------------------------------
+# Configuration options related to source browsing
+#---------------------------------------------------------------------------
+
+# If the SOURCE_BROWSER tag is set to YES then a list of source files will be
+# generated. Documented entities will be cross-referenced with these sources.
+#
+# Note: To get rid of all source code in the generated output, make sure that
+# also VERBATIM_HEADERS is set to NO.
+# The default value is: NO.
+
+SOURCE_BROWSER         = NO
+
+# Setting the INLINE_SOURCES tag to YES will include the body of functions,
+# classes and enums directly into the documentation.
+# The default value is: NO.
+
+INLINE_SOURCES         = NO
+
+# Setting the STRIP_CODE_COMMENTS tag to YES will instruct doxygen to hide any
+# special comment blocks from generated source code fragments. Normal C, C++ and
+# Fortran comments will always remain visible.
+# The default value is: YES.
+
+STRIP_CODE_COMMENTS    = YES
+
+# If the REFERENCED_BY_RELATION tag is set to YES then for each documented
+# entity all documented functions referencing it will be listed.
+# The default value is: NO.
+
+REFERENCED_BY_RELATION = NO
+
+# If the REFERENCES_RELATION tag is set to YES then for each documented function
+# all documented entities called/used by that function will be listed.
+# The default value is: NO.
+
+REFERENCES_RELATION    = NO
+
+# If the REFERENCES_LINK_SOURCE tag is set to YES and SOURCE_BROWSER tag is set
+# to YES then the hyperlinks from functions in REFERENCES_RELATION and
+# REFERENCED_BY_RELATION lists will link to the source code. Otherwise they will
+# link to the documentation.
+# The default value is: YES.
+
+REFERENCES_LINK_SOURCE = YES
+
+# If SOURCE_TOOLTIPS is enabled (the default) then hovering a hyperlink in the
+# source code will show a tooltip with additional information such as prototype,
+# brief description and links to the definition and documentation. Since this
+# will make the HTML file larger and loading of large files a bit slower, you
+# can opt to disable this feature.
+# The default value is: YES.
+# This tag requires that the tag SOURCE_BROWSER is set to YES.
+
+SOURCE_TOOLTIPS        = YES
+
+# If the USE_HTAGS tag is set to YES then the references to source code will
+# point to the HTML generated by the htags(1) tool instead of doxygen built-in
+# source browser. The htags tool is part of GNU's global source tagging system
+# (see https://www.gnu.org/software/global/global.html). You will need version
+# 4.8.6 or higher.
+#
+# To use it do the following:
+# - Install the latest version of global
+# - Enable SOURCE_BROWSER and USE_HTAGS in the configuration file
+# - Make sure the INPUT points to the root of the source tree
+# - Run doxygen as normal
+#
+# Doxygen will invoke htags (and that will in turn invoke gtags), so these
+# tools must be available from the command line (i.e. in the search path).
+#
+# The result: instead of the source browser generated by doxygen, the links to
+# source code will now point to the output of htags.
+# The default value is: NO.
+# This tag requires that the tag SOURCE_BROWSER is set to YES.
+
+USE_HTAGS              = NO
+
+# If the VERBATIM_HEADERS tag is set the YES then doxygen will generate a
+# verbatim copy of the header file for each class for which an include is
+# specified. Set to NO to disable this.
+# See also: Section \class.
+# The default value is: YES.
+
+VERBATIM_HEADERS       = YES
+
+#---------------------------------------------------------------------------
+# Configuration options related to the alphabetical class index
+#---------------------------------------------------------------------------
+
+# If the ALPHABETICAL_INDEX tag is set to YES, an alphabetical index of all
+# compounds will be generated. Enable this if the project contains a lot of
+# classes, structs, unions or interfaces.
+# The default value is: YES.
+
+ALPHABETICAL_INDEX     = YES
+
+# In case all classes in a project start with a common prefix, all classes will
+# be put under the same header in the alphabetical index. The IGNORE_PREFIX tag
+# can be used to specify a prefix (or a list of prefixes) that should be ignored
+# while generating the index headers.
+# This tag requires that the tag ALPHABETICAL_INDEX is set to YES.
+
+IGNORE_PREFIX          =
+
+#---------------------------------------------------------------------------
+# Configuration options related to the HTML output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_HTML tag is set to YES, doxygen will generate HTML output
+# The default value is: YES.
+
+GENERATE_HTML          = YES
+
+# The HTML_OUTPUT tag is used to specify where the HTML docs will be put. If a
+# relative path is entered the value of OUTPUT_DIRECTORY will be put in front of
+# it.
+# The default directory is: html.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+HTML_OUTPUT            = html
+
+# The HTML_FILE_EXTENSION tag can be used to specify the file extension for each
+# generated HTML page (for example: .htm, .php, .asp).
+# The default value is: .html.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+HTML_FILE_EXTENSION    = .html
+
+# The HTML_HEADER tag can be used to specify a user-defined HTML header file for
+# each generated HTML page. If the tag is left blank doxygen will generate a
+# standard header.
+#
+# To get valid HTML the header file that includes any scripts and style sheets
+# that doxygen needs, which is dependent on the configuration options used (e.g.
+# the setting GENERATE_TREEVIEW). It is highly recommended to start with a
+# default header using
+# doxygen -w html new_header.html new_footer.html new_stylesheet.css
+# YourConfigFile
+# and then modify the file new_header.html. See also section "Doxygen usage"
+# for information on how to generate the default header that doxygen normally
+# uses.
+# Note: The header is subject to change so you typically have to regenerate the
+# default header when upgrading to a newer version of doxygen. For a description
+# of the possible markers and block names see the documentation.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+HTML_HEADER            =
+
+# The HTML_FOOTER tag can be used to specify a user-defined HTML footer for each
+# generated HTML page. If the tag is left blank doxygen will generate a standard
+# footer. See HTML_HEADER for more information on how to generate a default
+# footer and what special commands can be used inside the footer. See also
+# section "Doxygen usage" for information on how to generate the default footer
+# that doxygen normally uses.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+HTML_FOOTER            =
+
+# The HTML_STYLESHEET tag can be used to specify a user-defined cascading style
+# sheet that is used by each HTML page. It can be used to fine-tune the look of
+# the HTML output. If left blank doxygen will generate a default style sheet.
+# See also section "Doxygen usage" for information on how to generate the style
+# sheet that doxygen normally uses.
+# Note: It is recommended to use HTML_EXTRA_STYLESHEET instead of this tag, as
+# it is more robust and this tag (HTML_STYLESHEET) will in the future become
+# obsolete.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+HTML_STYLESHEET        =
+
+# The HTML_EXTRA_STYLESHEET tag can be used to specify additional user-defined
+# cascading style sheets that are included after the standard style sheets
+# created by doxygen. Using this option one can overrule certain style aspects.
+# This is preferred over using HTML_STYLESHEET since it does not replace the
+# standard style sheet and is therefore more robust against future updates.
+# Doxygen will copy the style sheet files to the output directory.
+# Note: The order of the extra style sheet files is of importance (e.g. the last
+# style sheet in the list overrules the setting of the previous ones in the
+# list). For an example see the documentation.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+HTML_EXTRA_STYLESHEET  =
+
+# The HTML_EXTRA_FILES tag can be used to specify one or more extra images or
+# other source files which should be copied to the HTML output directory. Note
+# that these files will be copied to the base HTML output directory. Use the
+# $relpath^ marker in the HTML_HEADER and/or HTML_FOOTER files to load these
+# files. In the HTML_STYLESHEET file, use the file name only. Also note that the
+# files will be copied as-is; there are no commands or markers available.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+HTML_EXTRA_FILES       =
+
+# The HTML_COLORSTYLE_HUE tag controls the color of the HTML output. Doxygen
+# will adjust the colors in the style sheet and background images according to
+# this color. Hue is specified as an angle on a colorwheel, see
+# https://en.wikipedia.org/wiki/Hue for more information. For instance the value
+# 0 represents red, 60 is yellow, 120 is green, 180 is cyan, 240 is blue, 300
+# purple, and 360 is red again.
+# Minimum value: 0, maximum value: 359, default value: 220.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+HTML_COLORSTYLE_HUE    = 220
+
+# The HTML_COLORSTYLE_SAT tag controls the purity (or saturation) of the colors
+# in the HTML output. For a value of 0 the output will use grayscales only. A
+# value of 255 will produce the most vivid colors.
+# Minimum value: 0, maximum value: 255, default value: 100.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+HTML_COLORSTYLE_SAT    = 100
+
+# The HTML_COLORSTYLE_GAMMA tag controls the gamma correction applied to the
+# luminance component of the colors in the HTML output. Values below 100
+# gradually make the output lighter, whereas values above 100 make the output
+# darker. The value divided by 100 is the actual gamma applied, so 80 represents
+# a gamma of 0.8, The value 220 represents a gamma of 2.2, and 100 does not
+# change the gamma.
+# Minimum value: 40, maximum value: 240, default value: 80.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+HTML_COLORSTYLE_GAMMA  = 80
+
+# If the HTML_TIMESTAMP tag is set to YES then the footer of each generated HTML
+# page will contain the date and time when the page was generated. Setting this
+# to YES can help to show when doxygen was last run and thus if the
+# documentation is up to date.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+HTML_TIMESTAMP         = NO
+
+# If the HTML_DYNAMIC_MENUS tag is set to YES then the generated HTML
+# documentation will contain a main index with vertical navigation menus that
+# are dynamically created via JavaScript. If disabled, the navigation index will
+# consists of multiple levels of tabs that are statically embedded in every HTML
+# page. Disable this option to support browsers that do not have JavaScript,
+# like the Qt help browser.
+# The default value is: YES.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+HTML_DYNAMIC_MENUS     = YES
+
+# If the HTML_DYNAMIC_SECTIONS tag is set to YES then the generated HTML
+# documentation will contain sections that can be hidden and shown after the
+# page has loaded.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+HTML_DYNAMIC_SECTIONS  = NO
+
+# With HTML_INDEX_NUM_ENTRIES one can control the preferred number of entries
+# shown in the various tree structured indices initially; the user can expand
+# and collapse entries dynamically later on. Doxygen will expand the tree to
+# such a level that at most the specified number of entries are visible (unless
+# a fully collapsed tree already exceeds this amount). So setting the number of
+# entries 1 will produce a full collapsed tree by default. 0 is a special value
+# representing an infinite number of entries and will result in a full expanded
+# tree by default.
+# Minimum value: 0, maximum value: 9999, default value: 100.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+HTML_INDEX_NUM_ENTRIES = 100
+
+# If the GENERATE_DOCSET tag is set to YES, additional index files will be
+# generated that can be used as input for Apple's Xcode 3 integrated development
+# environment (see:
+# https://developer.apple.com/xcode/), introduced with OSX 10.5 (Leopard). To
+# create a documentation set, doxygen will generate a Makefile in the HTML
+# output directory. Running make will produce the docset in that directory and
+# running make install will install the docset in
+# ~/Library/Developer/Shared/Documentation/DocSets so that Xcode will find it at
+# startup. See https://developer.apple.com/library/archive/featuredarticles/Doxy
+# genXcode/_index.html for more information.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+GENERATE_DOCSET        = NO
+
+# This tag determines the name of the docset feed. A documentation feed provides
+# an umbrella under which multiple documentation sets from a single provider
+# (such as a company or product suite) can be grouped.
+# The default value is: Doxygen generated docs.
+# This tag requires that the tag GENERATE_DOCSET is set to YES.
+
+DOCSET_FEEDNAME        = "Doxygen generated docs"
+
+# This tag specifies a string that should uniquely identify the documentation
+# set bundle. This should be a reverse domain-name style string, e.g.
+# com.mycompany.MyDocSet. Doxygen will append .docset to the name.
+# The default value is: org.doxygen.Project.
+# This tag requires that the tag GENERATE_DOCSET is set to YES.
+
+DOCSET_BUNDLE_ID       = org.doxygen.Project
+
+# The DOCSET_PUBLISHER_ID tag specifies a string that should uniquely identify
+# the documentation publisher. This should be a reverse domain-name style
+# string, e.g. com.mycompany.MyDocSet.documentation.
+# The default value is: org.doxygen.Publisher.
+# This tag requires that the tag GENERATE_DOCSET is set to YES.
+
+DOCSET_PUBLISHER_ID    = org.doxygen.Publisher
+
+# The DOCSET_PUBLISHER_NAME tag identifies the documentation publisher.
+# The default value is: Publisher.
+# This tag requires that the tag GENERATE_DOCSET is set to YES.
+
+DOCSET_PUBLISHER_NAME  = Publisher
+
+# If the GENERATE_HTMLHELP tag is set to YES then doxygen generates three
+# additional HTML index files: index.hhp, index.hhc, and index.hhk. The
+# index.hhp is a project file that can be read by Microsoft's HTML Help Workshop
+# (see:
+# https://www.microsoft.com/en-us/download/details.aspx?id=21138) on Windows.
+#
+# The HTML Help Workshop contains a compiler that can convert all HTML output
+# generated by doxygen into a single compiled HTML file (.chm). Compiled HTML
+# files are now used as the Windows 98 help format, and will replace the old
+# Windows help format (.hlp) on all Windows platforms in the future. Compressed
+# HTML files also contain an index, a table of contents, and you can search for
+# words in the documentation. The HTML workshop also contains a viewer for
+# compressed HTML files.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+GENERATE_HTMLHELP      = NO
+
+# The CHM_FILE tag can be used to specify the file name of the resulting .chm
+# file. You can add a path in front of the file if the result should not be
+# written to the html output directory.
+# This tag requires that the tag GENERATE_HTMLHELP is set to YES.
+
+CHM_FILE               =
+
+# The HHC_LOCATION tag can be used to specify the location (absolute path
+# including file name) of the HTML help compiler (hhc.exe). If non-empty,
+# doxygen will try to run the HTML help compiler on the generated index.hhp.
+# The file has to be specified with full path.
+# This tag requires that the tag GENERATE_HTMLHELP is set to YES.
+
+HHC_LOCATION           =
+
+# The GENERATE_CHI flag controls if a separate .chi index file is generated
+# (YES) or that it should be included in the main .chm file (NO).
+# The default value is: NO.
+# This tag requires that the tag GENERATE_HTMLHELP is set to YES.
+
+GENERATE_CHI           = NO
+
+# The CHM_INDEX_ENCODING is used to encode HtmlHelp index (hhk), content (hhc)
+# and project file content.
+# This tag requires that the tag GENERATE_HTMLHELP is set to YES.
+
+CHM_INDEX_ENCODING     =
+
+# The BINARY_TOC flag controls whether a binary table of contents is generated
+# (YES) or a normal table of contents (NO) in the .chm file. Furthermore it
+# enables the Previous and Next buttons.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_HTMLHELP is set to YES.
+
+BINARY_TOC             = NO
+
+# The TOC_EXPAND flag can be set to YES to add extra items for group members to
+# the table of contents of the HTML help documentation and to the tree view.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_HTMLHELP is set to YES.
+
+TOC_EXPAND             = NO
+
+# If the GENERATE_QHP tag is set to YES and both QHP_NAMESPACE and
+# QHP_VIRTUAL_FOLDER are set, an additional index file will be generated that
+# can be used as input for Qt's qhelpgenerator to generate a Qt Compressed Help
+# (.qch) of the generated HTML documentation.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+GENERATE_QHP           = NO
+
+# If the QHG_LOCATION tag is specified, the QCH_FILE tag can be used to specify
+# the file name of the resulting .qch file. The path specified is relative to
+# the HTML output folder.
+# This tag requires that the tag GENERATE_QHP is set to YES.
+
+QCH_FILE               =
+
+# The QHP_NAMESPACE tag specifies the namespace to use when generating Qt Help
+# Project output. For more information please see Qt Help Project / Namespace
+# (see:
+# https://doc.qt.io/archives/qt-4.8/qthelpproject.html#namespace).
+# The default value is: org.doxygen.Project.
+# This tag requires that the tag GENERATE_QHP is set to YES.
+
+QHP_NAMESPACE          = org.doxygen.Project
+
+# The QHP_VIRTUAL_FOLDER tag specifies the namespace to use when generating Qt
+# Help Project output. For more information please see Qt Help Project / Virtual
+# Folders (see:
+# https://doc.qt.io/archives/qt-4.8/qthelpproject.html#virtual-folders).
+# The default value is: doc.
+# This tag requires that the tag GENERATE_QHP is set to YES.
+
+QHP_VIRTUAL_FOLDER     = doc
+
+# If the QHP_CUST_FILTER_NAME tag is set, it specifies the name of a custom
+# filter to add. For more information please see Qt Help Project / Custom
+# Filters (see:
+# https://doc.qt.io/archives/qt-4.8/qthelpproject.html#custom-filters).
+# This tag requires that the tag GENERATE_QHP is set to YES.
+
+QHP_CUST_FILTER_NAME   =
+
+# The QHP_CUST_FILTER_ATTRS tag specifies the list of the attributes of the
+# custom filter to add. For more information please see Qt Help Project / Custom
+# Filters (see:
+# https://doc.qt.io/archives/qt-4.8/qthelpproject.html#custom-filters).
+# This tag requires that the tag GENERATE_QHP is set to YES.
+
+QHP_CUST_FILTER_ATTRS  =
+
+# The QHP_SECT_FILTER_ATTRS tag specifies the list of the attributes this
+# project's filter section matches. Qt Help Project / Filter Attributes (see:
+# https://doc.qt.io/archives/qt-4.8/qthelpproject.html#filter-attributes).
+# This tag requires that the tag GENERATE_QHP is set to YES.
+
+QHP_SECT_FILTER_ATTRS  =
+
+# The QHG_LOCATION tag can be used to specify the location (absolute path
+# including file name) of Qt's qhelpgenerator. If non-empty doxygen will try to
+# run qhelpgenerator on the generated .qhp file.
+# This tag requires that the tag GENERATE_QHP is set to YES.
+
+QHG_LOCATION           =
+
+# If the GENERATE_ECLIPSEHELP tag is set to YES, additional index files will be
+# generated, together with the HTML files, they form an Eclipse help plugin. To
+# install this plugin and make it available under the help contents menu in
+# Eclipse, the contents of the directory containing the HTML and XML files needs
+# to be copied into the plugins directory of eclipse. The name of the directory
+# within the plugins directory should be the same as the ECLIPSE_DOC_ID value.
+# After copying Eclipse needs to be restarted before the help appears.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+GENERATE_ECLIPSEHELP   = NO
+
+# A unique identifier for the Eclipse help plugin. When installing the plugin
+# the directory name containing the HTML and XML files should also have this
+# name. Each documentation set should have its own identifier.
+# The default value is: org.doxygen.Project.
+# This tag requires that the tag GENERATE_ECLIPSEHELP is set to YES.
+
+ECLIPSE_DOC_ID         = org.doxygen.Project
+
+# If you want full control over the layout of the generated HTML pages it might
+# be necessary to disable the index and replace it with your own. The
+# DISABLE_INDEX tag can be used to turn on/off the condensed index (tabs) at top
+# of each HTML page. A value of NO enables the index and the value YES disables
+# it. Since the tabs in the index contain the same information as the navigation
+# tree, you can set this option to YES if you also set GENERATE_TREEVIEW to YES.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+DISABLE_INDEX          = NO
+
+# The GENERATE_TREEVIEW tag is used to specify whether a tree-like index
+# structure should be generated to display hierarchical information. If the tag
+# value is set to YES, a side panel will be generated containing a tree-like
+# index structure (just like the one that is generated for HTML Help). For this
+# to work a browser that supports JavaScript, DHTML, CSS and frames is required
+# (i.e. any modern browser). Windows users are probably better off using the
+# HTML help feature. Via custom style sheets (see HTML_EXTRA_STYLESHEET) one can
+# further fine-tune the look of the index. As an example, the default style
+# sheet generated by doxygen has an example that shows how to put an image at
+# the root of the tree instead of the PROJECT_NAME. Since the tree basically has
+# the same information as the tab index, you could consider setting
+# DISABLE_INDEX to YES when enabling this option.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+GENERATE_TREEVIEW      = YES
+
+# The ENUM_VALUES_PER_LINE tag can be used to set the number of enum values that
+# doxygen will group on one line in the generated HTML documentation.
+#
+# Note that a value of 0 will completely suppress the enum values from appearing
+# in the overview section.
+# Minimum value: 0, maximum value: 20, default value: 4.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+ENUM_VALUES_PER_LINE   = 4
+
+# If the treeview is enabled (see GENERATE_TREEVIEW) then this tag can be used
+# to set the initial width (in pixels) of the frame in which the tree is shown.
+# Minimum value: 0, maximum value: 1500, default value: 250.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+TREEVIEW_WIDTH         = 250
+
+# If the EXT_LINKS_IN_WINDOW option is set to YES, doxygen will open links to
+# external symbols imported via tag files in a separate window.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+EXT_LINKS_IN_WINDOW    = NO
+
+# If the HTML_FORMULA_FORMAT option is set to svg, doxygen will use the pdf2svg
+# tool (see https://github.com/dawbarton/pdf2svg) or inkscape (see
+# https://inkscape.org) to generate formulas as SVG images instead of PNGs for
+# the HTML output. These images will generally look nicer at scaled resolutions.
+# Possible values are: png (the default) and svg (looks nicer but requires the
+# pdf2svg or inkscape tool).
+# The default value is: png.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+HTML_FORMULA_FORMAT    = png
+
+# Use this tag to change the font size of LaTeX formulas included as images in
+# the HTML documentation. When you change the font size after a successful
+# doxygen run you need to manually remove any form_*.png images from the HTML
+# output directory to force them to be regenerated.
+# Minimum value: 8, maximum value: 50, default value: 10.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+FORMULA_FONTSIZE       = 10
+
+# Use the FORMULA_TRANSPARENT tag to determine whether or not the images
+# generated for formulas are transparent PNGs. Transparent PNGs are not
+# supported properly for IE 6.0, but are supported on all modern browsers.
+#
+# Note that when changing this option you need to delete any form_*.png files in
+# the HTML output directory before the changes have effect.
+# The default value is: YES.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+FORMULA_TRANSPARENT    = YES
+
+# The FORMULA_MACROFILE can contain LaTeX \newcommand and \renewcommand commands
+# to create new LaTeX commands to be used in formulas as building blocks. See
+# the section "Including formulas" for details.
+
+FORMULA_MACROFILE      =
+
+# Enable the USE_MATHJAX option to render LaTeX formulas using MathJax (see
+# https://www.mathjax.org) which uses client side JavaScript for the rendering
+# instead of using pre-rendered bitmaps. Use this if you do not have LaTeX
+# installed or if you want to formulas look prettier in the HTML output. When
+# enabled you may also need to install MathJax separately and configure the path
+# to it using the MATHJAX_RELPATH option.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+USE_MATHJAX            = NO
+
+# When MathJax is enabled you can set the default output format to be used for
+# the MathJax output. See the MathJax site (see:
+# http://docs.mathjax.org/en/v2.7-latest/output.html) for more details.
+# Possible values are: HTML-CSS (which is slower, but has the best
+# compatibility), NativeMML (i.e. MathML) and SVG.
+# The default value is: HTML-CSS.
+# This tag requires that the tag USE_MATHJAX is set to YES.
+
+MATHJAX_FORMAT         = HTML-CSS
+
+# When MathJax is enabled you need to specify the location relative to the HTML
+# output directory using the MATHJAX_RELPATH option. The destination directory
+# should contain the MathJax.js script. For instance, if the mathjax directory
+# is located at the same level as the HTML output directory, then
+# MATHJAX_RELPATH should be ../mathjax. The default value points to the MathJax
+# Content Delivery Network so you can quickly see the result without installing
+# MathJax. However, it is strongly recommended to install a local copy of
+# MathJax from https://www.mathjax.org before deployment.
+# The default value is: https://cdn.jsdelivr.net/npm/mathjax@2.
+# This tag requires that the tag USE_MATHJAX is set to YES.
+
+MATHJAX_RELPATH        = http://cdn.mathjax.org/mathjax/latest
+
+# The MATHJAX_EXTENSIONS tag can be used to specify one or more MathJax
+# extension names that should be enabled during MathJax rendering. For example
+# MATHJAX_EXTENSIONS = TeX/AMSmath TeX/AMSsymbols
+# This tag requires that the tag USE_MATHJAX is set to YES.
+
+MATHJAX_EXTENSIONS     =
+
+# The MATHJAX_CODEFILE tag can be used to specify a file with javascript pieces
+# of code that will be used on startup of the MathJax code. See the MathJax site
+# (see:
+# http://docs.mathjax.org/en/v2.7-latest/output.html) for more details. For an
+# example see the documentation.
+# This tag requires that the tag USE_MATHJAX is set to YES.
+
+MATHJAX_CODEFILE       =
+
+# When the SEARCHENGINE tag is enabled doxygen will generate a search box for
+# the HTML output. The underlying search engine uses javascript and DHTML and
+# should work on any modern browser. Note that when using HTML help
+# (GENERATE_HTMLHELP), Qt help (GENERATE_QHP), or docsets (GENERATE_DOCSET)
+# there is already a search function so this one should typically be disabled.
+# For large projects the javascript based search engine can be slow, then
+# enabling SERVER_BASED_SEARCH may provide a better solution. It is possible to
+# search using the keyboard; to jump to the search box use <access key> + S
+# (what the <access key> is depends on the OS and browser, but it is typically
+# <CTRL>, <ALT>/<option>, or both). Inside the search box use the <cursor down
+# key> to jump into the search results window, the results can be navigated
+# using the <cursor keys>. Press <Enter> to select an item or <escape> to cancel
+# the search. The filter options can be selected when the cursor is inside the
+# search box by pressing <Shift>+<cursor down>. Also here use the <cursor keys>
+# to select a filter and <Enter> or <escape> to activate or cancel the filter
+# option.
+# The default value is: YES.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+SEARCHENGINE           = YES
+
+# When the SERVER_BASED_SEARCH tag is enabled the search engine will be
+# implemented using a web server instead of a web client using JavaScript. There
+# are two flavors of web server based searching depending on the EXTERNAL_SEARCH
+# setting. When disabled, doxygen will generate a PHP script for searching and
+# an index file used by the script. When EXTERNAL_SEARCH is enabled the indexing
+# and searching needs to be provided by external tools. See the section
+# "External Indexing and Searching" for details.
+# The default value is: NO.
+# This tag requires that the tag SEARCHENGINE is set to YES.
+
+SERVER_BASED_SEARCH    = NO
+
+# When EXTERNAL_SEARCH tag is enabled doxygen will no longer generate the PHP
+# script for searching. Instead the search results are written to an XML file
+# which needs to be processed by an external indexer. Doxygen will invoke an
+# external search engine pointed to by the SEARCHENGINE_URL option to obtain the
+# search results.
+#
+# Doxygen ships with an example indexer (doxyindexer) and search engine
+# (doxysearch.cgi) which are based on the open source search engine library
+# Xapian (see:
+# https://xapian.org/).
+#
+# See the section "External Indexing and Searching" for details.
+# The default value is: NO.
+# This tag requires that the tag SEARCHENGINE is set to YES.
+
+EXTERNAL_SEARCH        = NO
+
+# The SEARCHENGINE_URL should point to a search engine hosted by a web server
+# which will return the search results when EXTERNAL_SEARCH is enabled.
+#
+# Doxygen ships with an example indexer (doxyindexer) and search engine
+# (doxysearch.cgi) which are based on the open source search engine library
+# Xapian (see:
+# https://xapian.org/). See the section "External Indexing and Searching" for
+# details.
+# This tag requires that the tag SEARCHENGINE is set to YES.
+
+SEARCHENGINE_URL       =
+
+# When SERVER_BASED_SEARCH and EXTERNAL_SEARCH are both enabled the unindexed
+# search data is written to a file for indexing by an external tool. With the
+# SEARCHDATA_FILE tag the name of this file can be specified.
+# The default file is: searchdata.xml.
+# This tag requires that the tag SEARCHENGINE is set to YES.
+
+SEARCHDATA_FILE        = searchdata.xml
+
+# When SERVER_BASED_SEARCH and EXTERNAL_SEARCH are both enabled the
+# EXTERNAL_SEARCH_ID tag can be used as an identifier for the project. This is
+# useful in combination with EXTRA_SEARCH_MAPPINGS to search through multiple
+# projects and redirect the results back to the right project.
+# This tag requires that the tag SEARCHENGINE is set to YES.
+
+EXTERNAL_SEARCH_ID     =
+
+# The EXTRA_SEARCH_MAPPINGS tag can be used to enable searching through doxygen
+# projects other than the one defined by this configuration file, but that are
+# all added to the same external search index. Each project needs to have a
+# unique id set via EXTERNAL_SEARCH_ID. The search mapping then maps the id of
+# to a relative location where the documentation can be found. The format is:
+# EXTRA_SEARCH_MAPPINGS = tagname1=loc1 tagname2=loc2 ...
+# This tag requires that the tag SEARCHENGINE is set to YES.
+
+EXTRA_SEARCH_MAPPINGS  =
+
+#---------------------------------------------------------------------------
+# Configuration options related to the LaTeX output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_LATEX tag is set to YES, doxygen will generate LaTeX output.
+# The default value is: YES.
+
+GENERATE_LATEX         = NO
+
+# The LATEX_OUTPUT tag is used to specify where the LaTeX docs will be put. If a
+# relative path is entered the value of OUTPUT_DIRECTORY will be put in front of
+# it.
+# The default directory is: latex.
+# This tag requires that the tag GENERATE_LATEX is set to YES.
+
+LATEX_OUTPUT           = latex
+
+# The LATEX_CMD_NAME tag can be used to specify the LaTeX command name to be
+# invoked.
+#
+# Note that when not enabling USE_PDFLATEX the default is latex when enabling
+# USE_PDFLATEX the default is pdflatex and when in the later case latex is
+# chosen this is overwritten by pdflatex. For specific output languages the
+# default can have been set differently, this depends on the implementation of
+# the output language.
+# This tag requires that the tag GENERATE_LATEX is set to YES.
+
+LATEX_CMD_NAME         = latex
+
+# The MAKEINDEX_CMD_NAME tag can be used to specify the command name to generate
+# index for LaTeX.
+# Note: This tag is used in the Makefile / make.bat.
+# See also: LATEX_MAKEINDEX_CMD for the part in the generated output file
+# (.tex).
+# The default file is: makeindex.
+# This tag requires that the tag GENERATE_LATEX is set to YES.
+
+MAKEINDEX_CMD_NAME     = makeindex
+
+# The LATEX_MAKEINDEX_CMD tag can be used to specify the command name to
+# generate index for LaTeX. In case there is no backslash (\) as first character
+# it will be automatically added in the LaTeX code.
+# Note: This tag is used in the generated output file (.tex).
+# See also: MAKEINDEX_CMD_NAME for the part in the Makefile / make.bat.
+# The default value is: makeindex.
+# This tag requires that the tag GENERATE_LATEX is set to YES.
+
+LATEX_MAKEINDEX_CMD    = makeindex
+
+# If the COMPACT_LATEX tag is set to YES, doxygen generates more compact LaTeX
+# documents. This may be useful for small projects and may help to save some
+# trees in general.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_LATEX is set to YES.
+
+COMPACT_LATEX          = NO
+
+# The PAPER_TYPE tag can be used to set the paper type that is used by the
+# printer.
+# Possible values are: a4 (210 x 297 mm), letter (8.5 x 11 inches), legal (8.5 x
+# 14 inches) and executive (7.25 x 10.5 inches).
+# The default value is: a4.
+# This tag requires that the tag GENERATE_LATEX is set to YES.
+
+PAPER_TYPE             = a4
+
+# The EXTRA_PACKAGES tag can be used to specify one or more LaTeX package names
+# that should be included in the LaTeX output. The package can be specified just
+# by its name or with the correct syntax as to be used with the LaTeX
+# \usepackage command. To get the times font for instance you can specify :
+# EXTRA_PACKAGES=times or EXTRA_PACKAGES={times}
+# To use the option intlimits with the amsmath package you can specify:
+# EXTRA_PACKAGES=[intlimits]{amsmath}
+# If left blank no extra packages will be included.
+# This tag requires that the tag GENERATE_LATEX is set to YES.
+
+EXTRA_PACKAGES         =
+
+# The LATEX_HEADER tag can be used to specify a personal LaTeX header for the
+# generated LaTeX document. The header should contain everything until the first
+# chapter. If it is left blank doxygen will generate a standard header. See
+# section "Doxygen usage" for information on how to let doxygen write the
+# default header to a separate file.
+#
+# Note: Only use a user-defined header if you know what you are doing! The
+# following commands have a special meaning inside the header: $title,
+# $datetime, $date, $doxygenversion, $projectname, $projectnumber,
+# $projectbrief, $projectlogo. Doxygen will replace $title with the empty
+# string, for the replacement values of the other commands the user is referred
+# to HTML_HEADER.
+# This tag requires that the tag GENERATE_LATEX is set to YES.
+
+LATEX_HEADER           =
+
+# The LATEX_FOOTER tag can be used to specify a personal LaTeX footer for the
+# generated LaTeX document. The footer should contain everything after the last
+# chapter. If it is left blank doxygen will generate a standard footer. See
+# LATEX_HEADER for more information on how to generate a default footer and what
+# special commands can be used inside the footer.
+#
+# Note: Only use a user-defined footer if you know what you are doing!
+# This tag requires that the tag GENERATE_LATEX is set to YES.
+
+LATEX_FOOTER           =
+
+# The LATEX_EXTRA_STYLESHEET tag can be used to specify additional user-defined
+# LaTeX style sheets that are included after the standard style sheets created
+# by doxygen. Using this option one can overrule certain style aspects. Doxygen
+# will copy the style sheet files to the output directory.
+# Note: The order of the extra style sheet files is of importance (e.g. the last
+# style sheet in the list overrules the setting of the previous ones in the
+# list).
+# This tag requires that the tag GENERATE_LATEX is set to YES.
+
+LATEX_EXTRA_STYLESHEET =
+
+# The LATEX_EXTRA_FILES tag can be used to specify one or more extra images or
+# other source files which should be copied to the LATEX_OUTPUT output
+# directory. Note that the files will be copied as-is; there are no commands or
+# markers available.
+# This tag requires that the tag GENERATE_LATEX is set to YES.
+
+LATEX_EXTRA_FILES      =
+
+# If the PDF_HYPERLINKS tag is set to YES, the LaTeX that is generated is
+# prepared for conversion to PDF (using ps2pdf or pdflatex). The PDF file will
+# contain links (just like the HTML output) instead of page references. This
+# makes the output suitable for online browsing using a PDF viewer.
+# The default value is: YES.
+# This tag requires that the tag GENERATE_LATEX is set to YES.
+
+PDF_HYPERLINKS         = YES
+
+# If the USE_PDFLATEX tag is set to YES, doxygen will use the engine as
+# specified with LATEX_CMD_NAME to generate the PDF file directly from the LaTeX
+# files. Set this option to YES, to get a higher quality PDF documentation.
+#
+# See also section LATEX_CMD_NAME for selecting the engine.
+# The default value is: YES.
+# This tag requires that the tag GENERATE_LATEX is set to YES.
+
+USE_PDFLATEX           = YES
+
+# If the LATEX_BATCHMODE tag is set to YES, doxygen will add the \batchmode
+# command to the generated LaTeX files. This will instruct LaTeX to keep running
+# if errors occur, instead of asking the user for help. This option is also used
+# when generating formulas in HTML.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_LATEX is set to YES.
+
+LATEX_BATCHMODE        = NO
+
+# If the LATEX_HIDE_INDICES tag is set to YES then doxygen will not include the
+# index chapters (such as File Index, Compound Index, etc.) in the output.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_LATEX is set to YES.
+
+LATEX_HIDE_INDICES     = NO
+
+# If the LATEX_SOURCE_CODE tag is set to YES then doxygen will include source
+# code with syntax highlighting in the LaTeX output.
+#
+# Note that which sources are shown also depends on other settings such as
+# SOURCE_BROWSER.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_LATEX is set to YES.
+
+LATEX_SOURCE_CODE      = NO
+
+# The LATEX_BIB_STYLE tag can be used to specify the style to use for the
+# bibliography, e.g. plainnat, or ieeetr. See
+# https://en.wikipedia.org/wiki/BibTeX and \cite for more info.
+# The default value is: plain.
+# This tag requires that the tag GENERATE_LATEX is set to YES.
+
+LATEX_BIB_STYLE        = plain
+
+# If the LATEX_TIMESTAMP tag is set to YES then the footer of each generated
+# page will contain the date and time when the page was generated. Setting this
+# to NO can help when comparing the output of multiple runs.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_LATEX is set to YES.
+
+LATEX_TIMESTAMP        = NO
+
+# The LATEX_EMOJI_DIRECTORY tag is used to specify the (relative or absolute)
+# path from which the emoji images will be read. If a relative path is entered,
+# it will be relative to the LATEX_OUTPUT directory. If left blank the
+# LATEX_OUTPUT directory will be used.
+# This tag requires that the tag GENERATE_LATEX is set to YES.
+
+LATEX_EMOJI_DIRECTORY  =
+
+#---------------------------------------------------------------------------
+# Configuration options related to the RTF output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_RTF tag is set to YES, doxygen will generate RTF output. The
+# RTF output is optimized for Word 97 and may not look too pretty with other RTF
+# readers/editors.
+# The default value is: NO.
+
+GENERATE_RTF           = NO
+
+# The RTF_OUTPUT tag is used to specify where the RTF docs will be put. If a
+# relative path is entered the value of OUTPUT_DIRECTORY will be put in front of
+# it.
+# The default directory is: rtf.
+# This tag requires that the tag GENERATE_RTF is set to YES.
+
+RTF_OUTPUT             = rtf
+
+# If the COMPACT_RTF tag is set to YES, doxygen generates more compact RTF
+# documents. This may be useful for small projects and may help to save some
+# trees in general.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_RTF is set to YES.
+
+COMPACT_RTF            = NO
+
+# If the RTF_HYPERLINKS tag is set to YES, the RTF that is generated will
+# contain hyperlink fields. The RTF file will contain links (just like the HTML
+# output) instead of page references. This makes the output suitable for online
+# browsing using Word or some other Word compatible readers that support those
+# fields.
+#
+# Note: WordPad (write) and others do not support links.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_RTF is set to YES.
+
+RTF_HYPERLINKS         = NO
+
+# Load stylesheet definitions from file. Syntax is similar to doxygen's
+# configuration file, i.e. a series of assignments. You only have to provide
+# replacements, missing definitions are set to their default value.
+#
+# See also section "Doxygen usage" for information on how to generate the
+# default style sheet that doxygen normally uses.
+# This tag requires that the tag GENERATE_RTF is set to YES.
+
+RTF_STYLESHEET_FILE    =
+
+# Set optional variables used in the generation of an RTF document. Syntax is
+# similar to doxygen's configuration file. A template extensions file can be
+# generated using doxygen -e rtf extensionFile.
+# This tag requires that the tag GENERATE_RTF is set to YES.
+
+RTF_EXTENSIONS_FILE    =
+
+# If the RTF_SOURCE_CODE tag is set to YES then doxygen will include source code
+# with syntax highlighting in the RTF output.
+#
+# Note that which sources are shown also depends on other settings such as
+# SOURCE_BROWSER.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_RTF is set to YES.
+
+RTF_SOURCE_CODE        = NO
+
+#---------------------------------------------------------------------------
+# Configuration options related to the man page output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_MAN tag is set to YES, doxygen will generate man pages for
+# classes and files.
+# The default value is: NO.
+
+GENERATE_MAN           = NO
+
+# The MAN_OUTPUT tag is used to specify where the man pages will be put. If a
+# relative path is entered the value of OUTPUT_DIRECTORY will be put in front of
+# it. A directory man3 will be created inside the directory specified by
+# MAN_OUTPUT.
+# The default directory is: man.
+# This tag requires that the tag GENERATE_MAN is set to YES.
+
+MAN_OUTPUT             = man
+
+# The MAN_EXTENSION tag determines the extension that is added to the generated
+# man pages. In case the manual section does not start with a number, the number
+# 3 is prepended. The dot (.) at the beginning of the MAN_EXTENSION tag is
+# optional.
+# The default value is: .3.
+# This tag requires that the tag GENERATE_MAN is set to YES.
+
+MAN_EXTENSION          = .3
+
+# The MAN_SUBDIR tag determines the name of the directory created within
+# MAN_OUTPUT in which the man pages are placed. If defaults to man followed by
+# MAN_EXTENSION with the initial . removed.
+# This tag requires that the tag GENERATE_MAN is set to YES.
+
+MAN_SUBDIR             =
+
+# If the MAN_LINKS tag is set to YES and doxygen generates man output, then it
+# will generate one additional man file for each entity documented in the real
+# man page(s). These additional files only source the real man page, but without
+# them the man command would be unable to find the correct page.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_MAN is set to YES.
+
+MAN_LINKS              = NO
+
+#---------------------------------------------------------------------------
+# Configuration options related to the XML output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_XML tag is set to YES, doxygen will generate an XML file that
+# captures the structure of the code including all documentation.
+# The default value is: NO.
+
+GENERATE_XML           = NO
+
+# The XML_OUTPUT tag is used to specify where the XML pages will be put. If a
+# relative path is entered the value of OUTPUT_DIRECTORY will be put in front of
+# it.
+# The default directory is: xml.
+# This tag requires that the tag GENERATE_XML is set to YES.
+
+XML_OUTPUT             = xml
+
+# If the XML_PROGRAMLISTING tag is set to YES, doxygen will dump the program
+# listings (including syntax highlighting and cross-referencing information) to
+# the XML output. Note that enabling this will significantly increase the size
+# of the XML output.
+# The default value is: YES.
+# This tag requires that the tag GENERATE_XML is set to YES.
+
+XML_PROGRAMLISTING     = YES
+
+# If the XML_NS_MEMB_FILE_SCOPE tag is set to YES, doxygen will include
+# namespace members in file scope as well, matching the HTML output.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_XML is set to YES.
+
+XML_NS_MEMB_FILE_SCOPE = NO
+
+#---------------------------------------------------------------------------
+# Configuration options related to the DOCBOOK output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_DOCBOOK tag is set to YES, doxygen will generate Docbook files
+# that can be used to generate PDF.
+# The default value is: NO.
+
+GENERATE_DOCBOOK       = NO
+
+# The DOCBOOK_OUTPUT tag is used to specify where the Docbook pages will be put.
+# If a relative path is entered the value of OUTPUT_DIRECTORY will be put in
+# front of it.
+# The default directory is: docbook.
+# This tag requires that the tag GENERATE_DOCBOOK is set to YES.
+
+DOCBOOK_OUTPUT         = docbook
+
+# If the DOCBOOK_PROGRAMLISTING tag is set to YES, doxygen will include the
+# program listings (including syntax highlighting and cross-referencing
+# information) to the DOCBOOK output. Note that enabling this will significantly
+# increase the size of the DOCBOOK output.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_DOCBOOK is set to YES.
+
+DOCBOOK_PROGRAMLISTING = NO
+
+#---------------------------------------------------------------------------
+# Configuration options for the AutoGen Definitions output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_AUTOGEN_DEF tag is set to YES, doxygen will generate an
+# AutoGen Definitions (see http://autogen.sourceforge.net/) file that captures
+# the structure of the code including all documentation. Note that this feature
+# is still experimental and incomplete at the moment.
+# The default value is: NO.
+
+GENERATE_AUTOGEN_DEF   = NO
+
+#---------------------------------------------------------------------------
+# Configuration options related to the Perl module output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_PERLMOD tag is set to YES, doxygen will generate a Perl module
+# file that captures the structure of the code including all documentation.
+#
+# Note that this feature is still experimental and incomplete at the moment.
+# The default value is: NO.
+
+GENERATE_PERLMOD       = NO
+
+# If the PERLMOD_LATEX tag is set to YES, doxygen will generate the necessary
+# Makefile rules, Perl scripts and LaTeX code to be able to generate PDF and DVI
+# output from the Perl module output.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_PERLMOD is set to YES.
+
+PERLMOD_LATEX          = NO
+
+# If the PERLMOD_PRETTY tag is set to YES, the Perl module output will be nicely
+# formatted so it can be parsed by a human reader. This is useful if you want to
+# understand what is going on. On the other hand, if this tag is set to NO, the
+# size of the Perl module output will be much smaller and Perl will parse it
+# just the same.
+# The default value is: YES.
+# This tag requires that the tag GENERATE_PERLMOD is set to YES.
+
+PERLMOD_PRETTY         = YES
+
+# The names of the make variables in the generated doxyrules.make file are
+# prefixed with the string contained in PERLMOD_MAKEVAR_PREFIX. This is useful
+# so different doxyrules.make files included by the same Makefile don't
+# overwrite each other's variables.
+# This tag requires that the tag GENERATE_PERLMOD is set to YES.
+
+PERLMOD_MAKEVAR_PREFIX =
+
+#---------------------------------------------------------------------------
+# Configuration options related to the preprocessor
+#---------------------------------------------------------------------------
+
+# If the ENABLE_PREPROCESSING tag is set to YES, doxygen will evaluate all
+# C-preprocessor directives found in the sources and include files.
+# The default value is: YES.
+
+ENABLE_PREPROCESSING   = YES
+
+# If the MACRO_EXPANSION tag is set to YES, doxygen will expand all macro names
+# in the source code. If set to NO, only conditional compilation will be
+# performed. Macro expansion can be done in a controlled way by setting
+# EXPAND_ONLY_PREDEF to YES.
+# The default value is: NO.
+# This tag requires that the tag ENABLE_PREPROCESSING is set to YES.
+
+MACRO_EXPANSION        = NO
+
+# If the EXPAND_ONLY_PREDEF and MACRO_EXPANSION tags are both set to YES then
+# the macro expansion is limited to the macros specified with the PREDEFINED and
+# EXPAND_AS_DEFINED tags.
+# The default value is: NO.
+# This tag requires that the tag ENABLE_PREPROCESSING is set to YES.
+
+EXPAND_ONLY_PREDEF     = NO
+
+# If the SEARCH_INCLUDES tag is set to YES, the include files in the
+# INCLUDE_PATH will be searched if a #include is found.
+# The default value is: YES.
+# This tag requires that the tag ENABLE_PREPROCESSING is set to YES.
+
+SEARCH_INCLUDES        = YES
+
+# The INCLUDE_PATH tag can be used to specify one or more directories that
+# contain include files that are not input files but should be processed by the
+# preprocessor.
+# This tag requires that the tag SEARCH_INCLUDES is set to YES.
+
+INCLUDE_PATH           =
+
+# You can use the INCLUDE_FILE_PATTERNS tag to specify one or more wildcard
+# patterns (like *.h and *.hpp) to filter out the header-files in the
+# directories. If left blank, the patterns specified with FILE_PATTERNS will be
+# used.
+# This tag requires that the tag ENABLE_PREPROCESSING is set to YES.
+
+INCLUDE_FILE_PATTERNS  =
+
+# The PREDEFINED tag can be used to specify one or more macro names that are
+# defined before the preprocessor is started (similar to the -D option of e.g.
+# gcc). The argument of the tag is a list of macros of the form: name or
+# name=definition (no spaces). If the definition and the "=" are omitted, "=1"
+# is assumed. To prevent a macro definition from being undefined via #undef or
+# recursively expanded use the := operator instead of the = operator.
+# This tag requires that the tag ENABLE_PREPROCESSING is set to YES.
+
+PREDEFINED             =
+
+# If the MACRO_EXPANSION and EXPAND_ONLY_PREDEF tags are set to YES then this
+# tag can be used to specify a list of macro names that should be expanded. The
+# macro definition that is found in the sources will be used. Use the PREDEFINED
+# tag if you want to use a different macro definition that overrules the
+# definition found in the source code.
+# This tag requires that the tag ENABLE_PREPROCESSING is set to YES.
+
+EXPAND_AS_DEFINED      =
+
+# If the SKIP_FUNCTION_MACROS tag is set to YES then doxygen's preprocessor will
+# remove all references to function-like macros that are alone on a line, have
+# an all uppercase name, and do not end with a semicolon. Such function macros
+# are typically used for boiler-plate code, and will confuse the parser if not
+# removed.
+# The default value is: YES.
+# This tag requires that the tag ENABLE_PREPROCESSING is set to YES.
+
+SKIP_FUNCTION_MACROS   = YES
+
+#---------------------------------------------------------------------------
+# Configuration options related to external references
+#---------------------------------------------------------------------------
+
+# The TAGFILES tag can be used to specify one or more tag files. For each tag
+# file the location of the external documentation should be added. The format of
+# a tag file without this location is as follows:
+# TAGFILES = file1 file2 ...
+# Adding location for the tag files is done as follows:
+# TAGFILES = file1=loc1 "file2 = loc2" ...
+# where loc1 and loc2 can be relative or absolute paths or URLs. See the
+# section "Linking to external documentation" for more information about the use
+# of tag files.
+# Note: Each tag file must have a unique name (where the name does NOT include
+# the path). If a tag file is not located in the directory in which doxygen is
+# run, you must also specify the path to the tagfile here.
+
+TAGFILES               =
+
+# When a file name is specified after GENERATE_TAGFILE, doxygen will create a
+# tag file that is based on the input files it reads. See section "Linking to
+# external documentation" for more information about the usage of tag files.
+
+GENERATE_TAGFILE       =
+
+# If the ALLEXTERNALS tag is set to YES, all external class will be listed in
+# the class index. If set to NO, only the inherited external classes will be
+# listed.
+# The default value is: NO.
+
+ALLEXTERNALS           = NO
+
+# If the EXTERNAL_GROUPS tag is set to YES, all external groups will be listed
+# in the modules index. If set to NO, only the current project's groups will be
+# listed.
+# The default value is: YES.
+
+EXTERNAL_GROUPS        = YES
+
+# If the EXTERNAL_PAGES tag is set to YES, all external pages will be listed in
+# the related pages index. If set to NO, only the current project's pages will
+# be listed.
+# The default value is: YES.
+
+EXTERNAL_PAGES         = YES
+
+#---------------------------------------------------------------------------
+# Configuration options related to the dot tool
+#---------------------------------------------------------------------------
+
+# If the CLASS_DIAGRAMS tag is set to YES, doxygen will generate a class diagram
+# (in HTML and LaTeX) for classes with base or super classes. Setting the tag to
+# NO turns the diagrams off. Note that this option also works with HAVE_DOT
+# disabled, but it is recommended to install and use dot, since it yields more
+# powerful graphs.
+# The default value is: YES.
+
+CLASS_DIAGRAMS         = YES
+
+# You can include diagrams made with dia in doxygen documentation. Doxygen will
+# then run dia to produce the diagram and insert it in the documentation. The
+# DIA_PATH tag allows you to specify the directory where the dia binary resides.
+# If left empty dia is assumed to be found in the default search path.
+
+DIA_PATH               =
+
+# If set to YES the inheritance and collaboration graphs will hide inheritance
+# and usage relations if the target is undocumented or is not a class.
+# The default value is: YES.
+
+HIDE_UNDOC_RELATIONS   = YES
+
+# If you set the HAVE_DOT tag to YES then doxygen will assume the dot tool is
+# available from the path. This tool is part of Graphviz (see:
+# http://www.graphviz.org/), a graph visualization toolkit from AT&T and Lucent
+# Bell Labs. The other options in this section have no effect if this option is
+# set to NO
+# The default value is: NO.
+
+HAVE_DOT               = YES
+
+# The DOT_NUM_THREADS specifies the number of dot invocations doxygen is allowed
+# to run in parallel. When set to 0 doxygen will base this on the number of
+# processors available in the system. You can set it explicitly to a value
+# larger than 0 to get control over the balance between CPU load and processing
+# speed.
+# Minimum value: 0, maximum value: 32, default value: 0.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+DOT_NUM_THREADS        = 0
+
+# When you want a differently looking font in the dot files that doxygen
+# generates you can specify the font name using DOT_FONTNAME. You need to make
+# sure dot is able to find the font, which can be done by putting it in a
+# standard location or by setting the DOTFONTPATH environment variable or by
+# setting DOT_FONTPATH to the directory containing the font.
+# The default value is: Helvetica.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+DOT_FONTNAME           = Helvetica
+
+# The DOT_FONTSIZE tag can be used to set the size (in points) of the font of
+# dot graphs.
+# Minimum value: 4, maximum value: 24, default value: 10.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+DOT_FONTSIZE           = 10
+
+# By default doxygen will tell dot to use the default font as specified with
+# DOT_FONTNAME. If you specify a different font using DOT_FONTNAME you can set
+# the path where dot can find it using this tag.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+DOT_FONTPATH           =
+
+# If the CLASS_GRAPH tag is set to YES then doxygen will generate a graph for
+# each documented class showing the direct and indirect inheritance relations.
+# Setting this tag to YES will force the CLASS_DIAGRAMS tag to NO.
+# The default value is: YES.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+CLASS_GRAPH            = YES
+
+# If the COLLABORATION_GRAPH tag is set to YES then doxygen will generate a
+# graph for each documented class showing the direct and indirect implementation
+# dependencies (inheritance, containment, and class references variables) of the
+# class with other documented classes.
+# The default value is: YES.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+COLLABORATION_GRAPH    = YES
+
+# If the GROUP_GRAPHS tag is set to YES then doxygen will generate a graph for
+# groups, showing the direct groups dependencies.
+# The default value is: YES.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+GROUP_GRAPHS           = YES
+
+# If the UML_LOOK tag is set to YES, doxygen will generate inheritance and
+# collaboration diagrams in a style similar to the OMG's Unified Modeling
+# Language.
+# The default value is: NO.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+UML_LOOK               = NO
+
+# If the UML_LOOK tag is enabled, the fields and methods are shown inside the
+# class node. If there are many fields or methods and many nodes the graph may
+# become too big to be useful. The UML_LIMIT_NUM_FIELDS threshold limits the
+# number of items for each type to make the size more manageable. Set this to 0
+# for no limit. Note that the threshold may be exceeded by 50% before the limit
+# is enforced. So when you set the threshold to 10, up to 15 fields may appear,
+# but if the number exceeds 15, the total amount of fields shown is limited to
+# 10.
+# Minimum value: 0, maximum value: 100, default value: 10.
+# This tag requires that the tag UML_LOOK is set to YES.
+
+UML_LIMIT_NUM_FIELDS   = 10
+
+# If the DOT_UML_DETAILS tag is set to NO, doxygen will show attributes and
+# methods without types and arguments in the UML graphs. If the DOT_UML_DETAILS
+# tag is set to YES, doxygen will add type and arguments for attributes and
+# methods in the UML graphs. If the DOT_UML_DETAILS tag is set to NONE, doxygen
+# will not generate fields with class member information in the UML graphs. The
+# class diagrams will look similar to the default class diagrams but using UML
+# notation for the relationships.
+# Possible values are: NO, YES and NONE.
+# The default value is: NO.
+# This tag requires that the tag UML_LOOK is set to YES.
+
+DOT_UML_DETAILS        = NO
+
+# The DOT_WRAP_THRESHOLD tag can be used to set the maximum number of characters
+# to display on a single line. If the actual line length exceeds this threshold
+# significantly it will wrapped across multiple lines. Some heuristics are apply
+# to avoid ugly line breaks.
+# Minimum value: 0, maximum value: 1000, default value: 17.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+DOT_WRAP_THRESHOLD     = 17
+
+# If the TEMPLATE_RELATIONS tag is set to YES then the inheritance and
+# collaboration graphs will show the relations between templates and their
+# instances.
+# The default value is: NO.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+TEMPLATE_RELATIONS     = NO
+
+# If the INCLUDE_GRAPH, ENABLE_PREPROCESSING and SEARCH_INCLUDES tags are set to
+# YES then doxygen will generate a graph for each documented file showing the
+# direct and indirect include dependencies of the file with other documented
+# files.
+# The default value is: YES.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+INCLUDE_GRAPH          = YES
+
+# If the INCLUDED_BY_GRAPH, ENABLE_PREPROCESSING and SEARCH_INCLUDES tags are
+# set to YES then doxygen will generate a graph for each documented file showing
+# the direct and indirect include dependencies of the file with other documented
+# files.
+# The default value is: YES.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+INCLUDED_BY_GRAPH      = YES
+
+# If the CALL_GRAPH tag is set to YES then doxygen will generate a call
+# dependency graph for every global function or class method.
+#
+# Note that enabling this option will significantly increase the time of a run.
+# So in most cases it will be better to enable call graphs for selected
+# functions only using the \callgraph command. Disabling a call graph can be
+# accomplished by means of the command \hidecallgraph.
+# The default value is: NO.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+CALL_GRAPH             = NO
+
+# If the CALLER_GRAPH tag is set to YES then doxygen will generate a caller
+# dependency graph for every global function or class method.
+#
+# Note that enabling this option will significantly increase the time of a run.
+# So in most cases it will be better to enable caller graphs for selected
+# functions only using the \callergraph command. Disabling a caller graph can be
+# accomplished by means of the command \hidecallergraph.
+# The default value is: NO.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+CALLER_GRAPH           = NO
+
+# If the GRAPHICAL_HIERARCHY tag is set to YES then doxygen will graphical
+# hierarchy of all classes instead of a textual one.
+# The default value is: YES.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+GRAPHICAL_HIERARCHY    = YES
+
+# If the DIRECTORY_GRAPH tag is set to YES then doxygen will show the
+# dependencies a directory has on other directories in a graphical way. The
+# dependency relations are determined by the #include relations between the
+# files in the directories.
+# The default value is: YES.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+DIRECTORY_GRAPH        = YES
+
+# The DOT_IMAGE_FORMAT tag can be used to set the image format of the images
+# generated by dot. For an explanation of the image formats see the section
+# output formats in the documentation of the dot tool (Graphviz (see:
+# http://www.graphviz.org/)).
+# Note: If you choose svg you need to set HTML_FILE_EXTENSION to xhtml in order
+# to make the SVG files visible in IE 9+ (other browsers do not have this
+# requirement).
+# Possible values are: png, jpg, gif, svg, png:gd, png:gd:gd, png:cairo,
+# png:cairo:gd, png:cairo:cairo, png:cairo:gdiplus, png:gdiplus and
+# png:gdiplus:gdiplus.
+# The default value is: png.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+DOT_IMAGE_FORMAT       = png
+
+# If DOT_IMAGE_FORMAT is set to svg, then this option can be set to YES to
+# enable generation of interactive SVG images that allow zooming and panning.
+#
+# Note that this requires a modern browser other than Internet Explorer. Tested
+# and working are Firefox, Chrome, Safari, and Opera.
+# Note: For IE 9+ you need to set HTML_FILE_EXTENSION to xhtml in order to make
+# the SVG files visible. Older versions of IE do not have SVG support.
+# The default value is: NO.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+INTERACTIVE_SVG        = NO
+
+# The DOT_PATH tag can be used to specify the path where the dot tool can be
+# found. If left blank, it is assumed the dot tool can be found in the path.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+DOT_PATH               =
+
+# The DOTFILE_DIRS tag can be used to specify one or more directories that
+# contain dot files that are included in the documentation (see the \dotfile
+# command).
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+DOTFILE_DIRS           =
+
+# The MSCFILE_DIRS tag can be used to specify one or more directories that
+# contain msc files that are included in the documentation (see the \mscfile
+# command).
+
+MSCFILE_DIRS           =
+
+# The DIAFILE_DIRS tag can be used to specify one or more directories that
+# contain dia files that are included in the documentation (see the \diafile
+# command).
+
+DIAFILE_DIRS           =
+
+# When using plantuml, the PLANTUML_JAR_PATH tag should be used to specify the
+# path where java can find the plantuml.jar file. If left blank, it is assumed
+# PlantUML is not used or called during a preprocessing step. Doxygen will
+# generate a warning when it encounters a \startuml command in this case and
+# will not generate output for the diagram.
+
+PLANTUML_JAR_PATH      =
+
+# When using plantuml, the PLANTUML_CFG_FILE tag can be used to specify a
+# configuration file for plantuml.
+
+PLANTUML_CFG_FILE      =
+
+# When using plantuml, the specified paths are searched for files specified by
+# the !include statement in a plantuml block.
+
+PLANTUML_INCLUDE_PATH  =
+
+# The DOT_GRAPH_MAX_NODES tag can be used to set the maximum number of nodes
+# that will be shown in the graph. If the number of nodes in a graph becomes
+# larger than this value, doxygen will truncate the graph, which is visualized
+# by representing a node as a red box. Note that doxygen if the number of direct
+# children of the root node in a graph is already larger than
+# DOT_GRAPH_MAX_NODES then the graph will not be shown at all. Also note that
+# the size of a graph can be further restricted by MAX_DOT_GRAPH_DEPTH.
+# Minimum value: 0, maximum value: 10000, default value: 50.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+DOT_GRAPH_MAX_NODES    = 50
+
+# The MAX_DOT_GRAPH_DEPTH tag can be used to set the maximum depth of the graphs
+# generated by dot. A depth value of 3 means that only nodes reachable from the
+# root by following a path via at most 3 edges will be shown. Nodes that lay
+# further from the root node will be omitted. Note that setting this option to 1
+# or 2 may greatly reduce the computation time needed for large code bases. Also
+# note that the size of a graph can be further restricted by
+# DOT_GRAPH_MAX_NODES. Using a depth of 0 means no depth restriction.
+# Minimum value: 0, maximum value: 1000, default value: 0.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+MAX_DOT_GRAPH_DEPTH    = 0
+
+# Set the DOT_TRANSPARENT tag to YES to generate images with a transparent
+# background. This is disabled by default, because dot on Windows does not seem
+# to support this out of the box.
+#
+# Warning: Depending on the platform used, enabling this option may lead to
+# badly anti-aliased labels on the edges of a graph (i.e. they become hard to
+# read).
+# The default value is: NO.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+DOT_TRANSPARENT        = NO
+
+# Set the DOT_MULTI_TARGETS tag to YES to allow dot to generate multiple output
+# files in one run (i.e. multiple -o and -T options on the command line). This
+# makes dot run faster, but since only newer versions of dot (>1.8.10) support
+# this, this feature is disabled by default.
+# The default value is: NO.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+DOT_MULTI_TARGETS      = NO
+
+# If the GENERATE_LEGEND tag is set to YES doxygen will generate a legend page
+# explaining the meaning of the various boxes and arrows in the dot generated
+# graphs.
+# The default value is: YES.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+GENERATE_LEGEND        = YES
+
+# If the DOT_CLEANUP tag is set to YES, doxygen will remove the intermediate
+# files that are used to generate the various graphs.
+#
+# Note: This setting is not only used for dot files but also for msc and
+# plantuml temporary files.
+# The default value is: YES.
+
+DOT_CLEANUP            = YES
diff --git a/vendor/highfive/doc/environment.yaml b/vendor/highfive/doc/environment.yaml
new file mode 100644
index 00000000..8f9fb810
--- /dev/null
+++ b/vendor/highfive/doc/environment.yaml
@@ -0,0 +1,7 @@
+channels:
+  - conda-forge
+dependencies:
+  - doxygen
+  - cmake
+  - graphviz
+  - hdf5
diff --git a/vendor/highfive/doc/mainpage.md b/vendor/highfive/doc/mainpage.md
new file mode 100644
index 00000000..ca431328
--- /dev/null
+++ b/vendor/highfive/doc/mainpage.md
@@ -0,0 +1,16 @@
+HighFive documentation {#mainpage}
+======================
+
+HighFive is a modern C++11 friendly interface for libhdf5.
+
+HighFive supports STL vector/string, eigen3, xtensor, Boost::UBLAS and Boost::Multi-array.
+
+It handles C++ from/to HDF5 automatic type mapping.
+
+HighFive does not require an additional library and supports both HDF5 thread safety and Parallel HDF5 (contrary to the official hdf5 cpp).
+
+HighFive has two interfaces: normal HighFive and H5Easy.
+
+H5Easy is a high-level interface composed by two main functions: H5Easy::load and H5Easy::dump.
+
+HighFive is a wrapper of HDF5 library which take care of ownership of your HDF5 objects.
diff --git a/vendor/highfive/include/highfive/H5Attribute.hpp b/vendor/highfive/include/highfive/H5Attribute.hpp
new file mode 100644
index 00000000..3a0898a0
--- /dev/null
+++ b/vendor/highfive/include/highfive/H5Attribute.hpp
@@ -0,0 +1,100 @@
+/*
+ *  Copyright (c), 2017, Ali Can Demiralp <ali.demiralp@rwth-aachen.de>
+ *
+ *  Distributed under the Boost Software License, Version 1.0.
+ *    (See accompanying file LICENSE_1_0.txt or copy at
+ *          http://www.boost.org/LICENSE_1_0.txt)
+ *
+ */
+#ifndef H5ATTRIBUTE_HPP
+#define H5ATTRIBUTE_HPP
+
+#include <vector>
+
+#include "H5DataSpace.hpp"
+#include "H5DataType.hpp"
+#include "H5Object.hpp"
+#include "bits/H5Path_traits.hpp"
+
+namespace HighFive {
+
+///
+/// \brief Class representing an attribute of a dataset or group
+///
+class Attribute : public Object,
+                  public PathTraits<Attribute> {
+  public:
+
+    const static ObjectType type = ObjectType::Attribute;
+
+    ///
+    /// \brief return the name of the current attribute
+    /// \return the name of the attribute
+    std::string getName() const;
+
+    size_t getStorageSize() const;
+
+    ///
+    /// \brief getDataType
+    /// \return return the datatype associated with this dataset
+    ///
+    DataType getDataType() const;
+
+    ///
+    /// \brief getSpace
+    /// \return return the dataspace associated with this dataset
+    ///
+    DataSpace getSpace() const;
+
+    ///
+    /// \brief getMemSpace
+    /// \return same than getSpace for DataSet, compatibility with Selection
+    /// class
+    ///
+    DataSpace getMemSpace() const;
+
+    ///
+    /// Read the attribute into a buffer
+    /// An exception is raised if the numbers of dimension of the buffer and of
+    /// the attribute are different
+    ///
+    /// The array type can be a N-pointer or a N-vector ( e.g int** integer two
+    /// dimensional array )
+    template <typename T>
+    void read(T& array) const;
+
+    ///
+    /// Read the attribute into a buffer
+    ///
+    template <typename T>
+    void read(T* array, const DataType& dtype = DataType()) const;
+
+    ///
+    /// Write the integrality N-dimension buffer to this attribute
+    /// An exception is raised if the numbers of dimension of the buffer and of
+    /// the attribute are different
+    ///
+    /// The array type can be a N-pointer or a N-vector ( e.g int** integer two
+    /// dimensional array )
+    template <typename T>
+    void write(const T& buffer);
+
+    ///
+    /// Write a buffer to this attribute
+    ///
+    template <typename T>
+    void write_raw(const T* buffer, const DataType& dtype = DataType());
+
+    // No empty attributes
+    Attribute() = delete;
+
+  private:
+    using Object::Object;
+
+    template <typename Derivate> friend class ::HighFive::AnnotateTraits;
+};
+
+}  // namespace HighFive
+
+
+#endif // H5ATTRIBUTE_HPP
diff --git a/vendor/highfive/include/highfive/H5DataSet.hpp b/vendor/highfive/include/highfive/H5DataSet.hpp
new file mode 100644
index 00000000..99a1555a
--- /dev/null
+++ b/vendor/highfive/include/highfive/H5DataSet.hpp
@@ -0,0 +1,108 @@
+/*
+ *  Copyright (c), 2017, Adrien Devresse <adrien.devresse@epfl.ch>
+ *
+ *  Distributed under the Boost Software License, Version 1.0.
+ *    (See accompanying file LICENSE_1_0.txt or copy at
+ *          http://www.boost.org/LICENSE_1_0.txt)
+ *
+ */
+#ifndef H5DATASET_HPP
+#define H5DATASET_HPP
+
+#include <vector>
+
+#include "H5DataSpace.hpp"
+#include "H5DataType.hpp"
+#include "H5Object.hpp"
+#include "bits/H5_definitions.hpp"
+#include "bits/H5Annotate_traits.hpp"
+#include "bits/H5Slice_traits.hpp"
+#include "bits/H5Path_traits.hpp"
+#include "bits/H5_definitions.hpp"
+
+namespace HighFive {
+
+///
+/// \brief Class representing a dataset.
+///
+class DataSet : public Object,
+                public SliceTraits<DataSet>,
+                public AnnotateTraits<DataSet>,
+                public PathTraits<DataSet> {
+  public:
+
+    const static ObjectType type = ObjectType::Dataset;
+
+    ///
+    /// \brief getStorageSize
+    /// \return returns the amount of storage allocated for a dataset.
+    ///
+    uint64_t getStorageSize() const;
+
+    ///
+    /// \brief getOffset
+    /// \return returns DataSet address in file
+    ///
+    uint64_t getOffset() const;
+
+    ///
+    /// \brief getDataType
+    /// \return return the datatype associated with this dataset
+    ///
+    DataType getDataType() const;
+
+    ///
+    /// \brief getSpace
+    /// \return return the dataspace associated with this dataset
+    ///
+    DataSpace getSpace() const;
+
+    ///
+    /// \brief getMemSpace
+    /// \return same than getSpace for DataSet, compatibility with Selection
+    /// class
+    ///
+    DataSpace getMemSpace() const;
+
+
+    /// \brief Change the size of the dataset
+    ///
+    /// This requires that the dataset was created with chunking, and you would
+    /// generally want to have set a larger maxdims setting
+    /// \param dims New size of the dataset
+    void resize(const std::vector<size_t>& dims);
+
+
+    /// \brief Get the dimensions of the whole DataSet.
+    ///       This is a shorthand for getSpace().getDimensions()
+    /// \return The shape of the current HighFive::DataSet
+    ///
+    inline std::vector<size_t> getDimensions() const {
+        return getSpace().getDimensions();
+    }
+
+    /// \brief Get the total number of elements in the current dataset.
+    ///       E.g. 2x2x2 matrix has size 8.
+    ///       This is a shorthand for getSpace().getTotalCount()
+    /// \return The shape of the current HighFive::DataSet
+    ///
+    inline size_t getElementCount() const {
+        return getSpace().getElementCount();
+    }
+
+    H5_DEPRECATED("Default constructor creates unsafe uninitialized objects")
+    DataSet() = default;
+
+  protected:
+    using Object::Object;  // bring DataSet(hid_t)
+
+    DataSet(Object&& o) noexcept : Object(std::move(o)) {}
+
+    friend class Reference;
+    template <typename Derivate> friend class NodeTraits;
+
+};
+
+}  // namespace HighFive
+
+#endif // H5DATASET_HPP
diff --git a/vendor/highfive/include/highfive/H5DataSpace.hpp b/vendor/highfive/include/highfive/H5DataSpace.hpp
new file mode 100644
index 00000000..5e996066
--- /dev/null
+++ b/vendor/highfive/include/highfive/H5DataSpace.hpp
@@ -0,0 +1,134 @@
+/*
+ *  Copyright (c), 2017, Adrien Devresse <adrien.devresse@epfl.ch>
+ *
+ *  Distributed under the Boost Software License, Version 1.0.
+ *    (See accompanying file LICENSE_1_0.txt or copy at
+ *          http://www.boost.org/LICENSE_1_0.txt)
+ *
+ */
+#ifndef H5DATASPACE_HPP
+#define H5DATASPACE_HPP
+
+#include <vector>
+#include <array>
+#include <cstdint>
+#include <type_traits>
+#include <initializer_list>
+
+#ifdef H5_USE_BOOST
+// starting Boost 1.64, serialization header must come before ublas
+#include <boost/serialization/vector.hpp>
+#include <boost/multi_array.hpp>
+#include <boost/numeric/ublas/matrix.hpp>
+#endif
+
+#ifdef H5_USE_EIGEN
+#include <Eigen/Eigen>
+#endif
+
+#include "H5Object.hpp"
+#include "bits/H5_definitions.hpp"
+
+namespace HighFive {
+
+///
+/// \brief Class representing the space (dimensions) of a dataset
+///
+class DataSpace : public Object {
+  public:
+
+    const static ObjectType type = ObjectType::DataSpace;
+
+    static const size_t UNLIMITED = SIZE_MAX;
+
+    /// dataspace type
+    enum DataspaceType {
+        datascape_scalar,
+        datascape_null
+        // simple dataspace are handle directly from their dimensions
+    };
+
+    /// create a dataspace of N-dimensions
+    /// Each dimension is configured this way
+    ///  size(dim1) = vec[0]
+    ///  size(dim2) = vec[1]
+    ///  etc...
+    explicit DataSpace(const std::vector<size_t>& dims);
+
+    // create a dataspace of N-dimensions
+    template <size_t N>
+    explicit DataSpace(const std::array<size_t, N>& dims);
+
+    /// Make sure that DataSpace({1,2,3}) works on GCC. This is
+    /// the shortcut form of the vector initializer, but one some compilers (gcc)
+    /// this does not resolve correctly without this constructor.
+    DataSpace(const std::initializer_list<size_t>& items);
+
+    /// Allow directly listing 1 or more dimensions to initialize,
+    /// that is, DataSpace(1,2) means DataSpace(std::vector<size_t>{1,2}).
+    template<typename... Args>
+    explicit DataSpace(size_t dim1, Args... dims);
+
+    /// Create a dataspace from an iterator pair
+    ///
+    /// Explicitly disable DataSpace(int_like, int_like) from trying to use this constructor
+    template <typename IT, typename = typename std::enable_if<!std::is_integral<IT>::value,IT>::type>
+    DataSpace(const IT begin,
+              const IT end);
+
+    /// \brief Create a resizable N-dimensional dataspace
+    /// \param dims Initial size of dataspace
+    /// \param maxdims Maximum size of the dataspace
+    explicit DataSpace(const std::vector<size_t>& dims,
+                       const std::vector<size_t>& maxdims);
+
+    ///
+    /// \brief DataSpace create a scalar dataspace or a null dataset
+    ///
+    explicit DataSpace(DataspaceType dtype);
+
+    /// Create a new DataSpace
+    ///  with a different id available for modifications
+    DataSpace clone() const;
+
+    ///
+    /// \brief getNumberDimensions
+    /// \return the number of dimensions in the current dataspace
+    ///
+    size_t getNumberDimensions() const;
+
+    /// \brief getDimensions
+    /// \return return a vector of N-element, each element is the size of the
+    /// associated dataset dimension
+    std::vector<size_t> getDimensions() const;
+
+    /// \brief getElementCount
+    /// \return the total number of elements in the dataspace
+    size_t getElementCount() const;
+
+    /// \brief getMaxDimensions
+    /// \return return a vector of N-element, each element is the size of the
+    /// associated dataset maximum dimension
+    std::vector<size_t> getMaxDimensions() const;
+
+    /// Create a dataspace matching a type accepted by details::inspector
+    template <typename T>
+    static DataSpace From(const T& value);
+
+    template <std::size_t N, std::size_t Width>
+    static DataSpace FromCharArrayStrings(const char(&)[N][Width]);
+
+  protected:
+    DataSpace() = default;
+
+    friend class Attribute;
+    friend class File;
+    friend class DataSet;
+};
+
+}  // namespace HighFive
+
+// We include bits right away since DataSpace is user-constructible
+#include "bits/H5Dataspace_misc.hpp"
+
+#endif // H5DATASPACE_HPP
diff --git a/vendor/highfive/include/highfive/H5DataType.hpp b/vendor/highfive/include/highfive/H5DataType.hpp
new file mode 100644
index 00000000..724d4567
--- /dev/null
+++ b/vendor/highfive/include/highfive/H5DataType.hpp
@@ -0,0 +1,371 @@
+/*
+ *  Copyright (c), 2017, Adrien Devresse <adrien.devresse@epfl.ch>
+ *
+ *  Distributed under the Boost Software License, Version 1.0.
+ *    (See accompanying file LICENSE_1_0.txt or copy at
+ *          http://www.boost.org/LICENSE_1_0.txt)
+ *
+ */
+#ifndef H5DATATYPE_HPP
+#define H5DATATYPE_HPP
+
+#include <vector>
+
+#include "H5Object.hpp"
+#include "bits/H5Utils.hpp"
+
+namespace HighFive {
+
+
+///
+/// \brief Enum of Fundamental data classes
+///
+enum class DataTypeClass {
+    Time,
+    Integer,
+    Float,
+    String,
+    BitField,
+    Opaque,
+    Compound,
+    Reference,
+    Enum,
+    VarLen,
+    Array,
+    Invalid
+};
+
+
+///
+/// \brief HDF5 Data Type
+///
+class DataType : public Object {
+  public:
+
+    bool operator==(const DataType& other) const;
+
+    bool operator!=(const DataType& other) const;
+
+    ///
+    /// \brief Return the fundamental type.
+    ///
+    DataTypeClass getClass() const;
+
+    ///
+    /// \brief Returns the length (in bytes) of this type elements
+    ///
+    /// Notice that the size of variable length sequences may have limited applicability
+    ///   given that it refers to the size of the control structure. For info see
+    ///   https://support.hdfgroup.org/HDF5/doc/RM/RM_H5T.html#Datatype-GetSize
+    size_t getSize() const;
+
+    ///
+    /// \brief Returns a friendly description of the type (e.g. Float32)
+    ///
+    std::string string() const;
+
+    ///
+    /// \brief Returns whether the type is a variable-length string
+    ///
+    bool isVariableStr() const;
+
+    ///
+    /// \brief Returns whether the type is a fixed-length string
+    ///
+    bool isFixedLenStr() const;
+
+    ///
+    /// \brief Check the DataType was default constructed.
+    /// Such value might represent auto-detection of the datatype from a buffer
+    ///
+    bool empty() const noexcept;
+
+    /// \brief Returns whether the type is a Reference
+    bool isReference() const;
+
+  protected:
+    using Object::Object;
+
+    friend class Attribute;
+    friend class File;
+    friend class DataSet;
+};
+
+///
+/// \brief create an HDF5 DataType from a C++ type
+///
+///  Support only basic data type
+///
+template <typename T>
+class AtomicType : public DataType {
+  public:
+    AtomicType();
+
+    typedef T basic_type;
+};
+
+
+///
+/// \brief Create a compound HDF5 datatype
+///
+class CompoundType : public DataType {
+public:
+    ///
+    /// \brief Use for defining a sub-type of compound type
+    struct member_def {
+        member_def(std::string t_name, DataType t_base_type, size_t t_offset = 0)
+            : name(std::move(t_name))
+            , base_type(std::move(t_base_type))
+            , offset(t_offset) {}
+        std::string name;
+        DataType base_type;
+        size_t offset;
+    };
+
+    CompoundType(const CompoundType& other) = default;
+
+    ///
+    /// \brief Initializes a compound type from a vector of member definitions
+    /// \param t_members
+    /// \param size
+    inline CompoundType(const std::vector<member_def>& t_members, size_t size = 0)
+        : members(t_members) {
+        create(size);
+    }
+    inline CompoundType(std::vector<member_def>&& t_members, size_t size = 0)
+        : members(std::move(t_members)) {
+        create(size);
+    }
+    inline CompoundType(const std::initializer_list<member_def>& t_members,
+                        size_t size = 0)
+        : members(t_members) {
+        create(size);
+    }
+
+    /// \brief Commit datatype into the given Object
+    /// \param object Location to commit object into
+    /// \param name Name to give the datatype
+    inline void commit(const Object& object, const std::string& name) const;
+
+    /// \brief Get read access to the CompoundType members
+    inline const std::vector<member_def>& getMembers() const noexcept {
+        return members;
+    }
+
+private:
+
+    /// A vector of the member_def members of this CompoundType
+    std::vector<member_def> members;
+
+    /// \brief Automatically create the type from the set of members
+    ///        using standard struct alignment.
+    /// \param size Total size of data type
+    void create(size_t size = 0);
+};
+
+///
+/// \brief Create a enum HDF5 datatype
+///
+/// \code{.cpp}
+/// enum class Position {
+///     FIRST = 1,
+///     SECOND = 2,
+/// };
+///
+/// EnumType<Position> create_enum_position() {
+///     return {{"FIRST", Position::FIRST},
+///             {"SECOND", Position::SECOND}};
+/// }
+///
+/// // You have to register the type inside HighFive
+/// HIGHFIVE_REGISTER_TYPE(Position, create_enum_position)
+///
+/// void write_first(H5::File& file) {
+///     auto dataset = file.createDataSet("/foo", Position::FIRST);
+/// }
+/// \endcode
+template<typename T>
+class EnumType: public DataType {
+public:
+    ///
+    /// \brief Use for defining a member of enum type
+    struct member_def {
+        member_def(const std::string& t_name, T t_value)
+            : name(t_name)
+            , value(std::move(t_value)) {}
+        std::string name;
+        T value;
+    };
+
+    EnumType(const EnumType& other) = default;
+
+    EnumType(const std::vector<member_def>& t_members)
+        : members(t_members) {
+        create();
+    }
+
+    EnumType(std::initializer_list<member_def> t_members)
+        : EnumType(std::vector<member_def>({t_members})) {}
+
+    /// \brief Commit datatype into the given Object
+    /// \param object Location to commit object into
+    /// \param name Name to give the datatype
+    void commit(const Object& object, const std::string& name) const;
+
+private:
+    std::vector<member_def> members;
+
+    void create();
+};
+
+
+/// \brief Create a DataType instance representing type T
+template <typename T>
+DataType create_datatype();
+
+
+/// \brief Create a DataType instance representing type T and perform a sanity check on its size
+template <typename T>
+DataType create_and_check_datatype();
+
+
+///
+/// \brief A structure representing a set of fixed-length strings
+///
+/// Although fixed-len arrays can be created 'raw' without the need for
+/// this structure, to retrieve results efficiently it must be used.
+///
+template <std::size_t N>
+class FixedLenStringArray {
+  public:
+    FixedLenStringArray() = default;
+
+    ///
+    /// \brief Create a FixedStringArray from a raw contiguous buffer
+    ///
+    FixedLenStringArray(const char array[][N], std::size_t length);
+
+    ///
+    /// \brief Create a FixedStringArray from a sequence of strings.
+    ///
+    /// Such conversion involves a copy, original vector is not modified
+    ///
+    explicit FixedLenStringArray(const std::vector<std::string> & vec);
+
+    FixedLenStringArray(const std::string* iter_begin, const std::string* iter_end);
+
+    FixedLenStringArray(const std::initializer_list<std::string> &);
+
+    ///
+    /// \brief Append an std::string to the buffer structure
+    ///
+    void push_back(const std::string&);
+
+    void push_back(const std::array<char, N>&);
+
+    ///
+    /// \brief Retrieve a string from the structure as std::string
+    ///
+    std::string getString(std::size_t index) const;
+
+    // Container interface
+    inline const char* operator[](std::size_t i) const noexcept {
+        return datavec[i].data();
+    }
+    inline const char* at(std::size_t i) const {
+        return datavec.at(i).data();
+    }
+    inline bool empty() const noexcept {
+        return datavec.empty();
+    }
+    inline std::size_t size() const noexcept {
+        return datavec.size();
+    }
+    inline void resize(std::size_t n) {
+        datavec.resize(n);
+    }
+    inline const char* front() const {
+        return datavec.front().data();
+    }
+    inline const char* back() const {
+        return datavec.back().data();
+    }
+    inline char* data() noexcept {
+        return datavec[0].data();
+    }
+    inline const char* data() const noexcept {
+        return datavec[0].data();
+    }
+
+  private:
+    using vector_t = typename std::vector<std::array<char, N>>;
+
+  public:
+    // Use the underlying iterator
+    using iterator = typename vector_t::iterator;
+    using const_iterator = typename vector_t::const_iterator;
+    using reverse_iterator = typename vector_t::reverse_iterator;
+    using const_reverse_iterator = typename vector_t::const_reverse_iterator;
+    using value_type = typename vector_t::value_type;
+
+    inline iterator begin() noexcept {
+        return datavec.begin();
+    }
+    inline iterator end() noexcept {
+        return datavec.end();
+    }
+    inline const_iterator begin() const noexcept {
+        return datavec.begin();
+    }
+    inline const_iterator cbegin() const noexcept {
+        return datavec.cbegin();
+    }
+    inline const_iterator end() const noexcept {
+        return datavec.end();
+    }
+    inline const_iterator cend() const noexcept {
+        return datavec.cend();
+    }
+    inline reverse_iterator rbegin() noexcept {
+        return datavec.rbegin();
+    }
+    inline reverse_iterator rend() noexcept {
+        return datavec.rend();
+    }
+    inline const_reverse_iterator rbegin() const noexcept {
+        return datavec.rbegin();
+    }
+    inline const_reverse_iterator rend() const noexcept {
+        return datavec.rend();
+    }
+
+  private:
+    vector_t datavec;
+};
+
+}  // namespace HighFive
+
+
+/// \brief Macro to extend datatype of HighFive
+///
+/// This macro has to be called outside of any namespace.
+///
+/// \code{.cpp}
+/// enum FooBar { FOO = 1, BAR = 2 };
+/// EnumType create_enum_foobar() {
+///    return EnumType<FooBar>({{"FOO", FooBar::FOO},
+///                             {"BAR", FooBar::BAR}});
+/// }
+/// HIGHFIVE_REGISTER_TYPE(FooBar, create_enum_foobar)
+/// \endcode
+#define HIGHFIVE_REGISTER_TYPE(type, function) \
+    namespace HighFive {                       \
+    template<>                                 \
+    DataType create_datatype<type>() {         \
+        return function();                     \
+    }                                          \
+    }
+
+#include "bits/H5DataType_misc.hpp"
+
+#endif // H5DATATYPE_HPP
diff --git a/vendor/highfive/include/highfive/H5Easy.hpp b/vendor/highfive/include/highfive/H5Easy.hpp
new file mode 100644
index 00000000..f2225927
--- /dev/null
+++ b/vendor/highfive/include/highfive/H5Easy.hpp
@@ -0,0 +1,411 @@
+/*
+ *  Copyright (c), 2017, Adrien Devresse <adrien.devresse@epfl.ch>
+ *
+ *  Distributed under the Boost Software License, Version 1.0.
+ *    (See accompanying file LICENSE_1_0.txt or copy at
+ *          http://www.boost.org/LICENSE_1_0.txt)
+ *
+ */
+
+/// \brief
+/// Read/dump DataSets or Attribute using a minimalistic syntax.
+/// To this end, the functions are templated, and accept:
+/// - Any type accepted by HighFive
+/// - Eigen objects
+/// - xtensor objects
+/// - OpenCV objects
+
+#ifndef H5EASY_HPP
+#define H5EASY_HPP
+
+#include <string>
+#include <vector>
+
+// optionally enable xtensor plug-in and load the library
+#ifdef XTENSOR_VERSION_MAJOR
+#ifndef H5_USE_XTENSOR
+#define H5_USE_XTENSOR
+#endif
+#endif
+
+#ifdef H5_USE_XTENSOR
+#include <xtensor/xarray.hpp>
+#include <xtensor/xtensor.hpp>
+#endif
+
+// optionally enable Eigen plug-in and load the library
+#ifdef EIGEN_WORLD_VERSION
+#ifndef H5_USE_EIGEN
+#define H5_USE_EIGEN
+#endif
+#endif
+
+#ifdef H5_USE_EIGEN
+#include <Eigen/Eigen>
+#endif
+
+// optionally enable OpenCV plug-in and load the library
+#ifdef CV_MAJOR_VERSION
+#ifndef H5_USE_OPENCV
+#define H5_USE_OPENCV
+#endif
+#endif
+
+#ifdef H5_USE_OPENCV
+#include <opencv2/opencv.hpp>
+#endif
+
+#include "H5File.hpp"
+
+namespace H5Easy {
+
+using HighFive::Attribute;
+using HighFive::AtomicType;
+using HighFive::Chunking;
+using HighFive::DataSet;
+using HighFive::DataSetCreateProps;
+using HighFive::DataSpace;
+using HighFive::Deflate;
+using HighFive::Exception;
+using HighFive::File;
+using HighFive::ObjectType;
+using HighFive::Shuffle;
+
+///
+/// \brief Write mode for DataSets
+enum class DumpMode {
+    Create = 0, /*!< Dump only if DataSet does not exist, otherwise throw. */
+    Overwrite = 1 /*!< Create or overwrite if DataSet of correct shape exists, otherwise throw. */
+};
+
+///
+/// \brief Signal to enable/disable automatic flushing after write operations.
+enum class Flush
+{
+    False = 0, /*!< No automatic flushing. */
+    True = 1 /*!< Automatic flushing. */
+};
+
+///
+/// \brief Signal to set compression level for written DataSets.
+class Compression
+{
+public:
+
+    ///
+    /// \brief Enable compression with the highest compression level (9).
+    /// or disable compression (set compression level to 0).
+    ///
+    /// \param enable ``true`` to enable with highest compression level
+    explicit Compression(bool enable = true);
+
+    ///
+    /// \brief Set compression level.
+    ///
+    /// \param level the compression level
+    template <class T>
+    Compression(T level);
+
+    ///
+    /// \brief Return compression level.
+    inline unsigned get() const;
+
+private:
+    unsigned m_compression_level;
+};
+
+///
+/// \brief Define options for dumping data.
+///
+/// By default:
+/// - DumpMode::Create
+/// - Flush::True
+/// - Compression: false
+/// - ChunkSize: automatic
+class DumpOptions
+{
+public:
+    ///
+    /// \brief Constructor: accept all default settings.
+    DumpOptions() = default;
+
+    ///
+    /// \brief Constructor: overwrite (some of the) defaults.
+    /// \param args any of DumpMode(), Flush(), Compression() in arbitrary number and order.
+    template <class... Args>
+    DumpOptions(Args... args)
+    {
+        set(args...);
+    }
+
+    ///
+    /// \brief Overwrite H5Easy::DumpMode setting.
+    /// \param mode: DumpMode.
+    inline void set(DumpMode mode);
+
+    ///
+    /// \brief Overwrite H5Easy::Flush setting.
+    /// \param mode Flush.
+    inline void set(Flush mode);
+
+    ///
+    /// \brief Overwrite H5Easy::Compression setting.
+    /// \param level Compression.
+    inline void set(const Compression& level);
+
+    ///
+    /// \brief Overwrite any setting(s).
+    /// \param arg any of DumpMode(), Flush(), Compression in arbitrary number and order.
+    /// \param args any of DumpMode(), Flush(), Compression in arbitrary number and order.
+    template <class T, class... Args>
+    inline void set(T arg, Args... args);
+
+    ///
+    /// \brief Set chunk-size. If the input is rank (size) zero, automatic chunking is enabled.
+    /// \param shape Chunk size along each dimension.
+    template <class T>
+    inline void setChunkSize(const std::vector<T>& shape);
+
+    ///
+    /// \brief Set chunk-size. If the input is rank (size) zero, automatic chunking is enabled.
+    /// \param shape Chunk size along each dimension.
+    inline void setChunkSize(std::initializer_list<size_t> shape);
+
+    ///
+    /// \brief Get overwrite-mode.
+    /// \return bool
+    inline bool overwrite() const;
+
+    ///
+    /// \brief Get flush-mode.
+    /// \return bool
+    inline bool flush() const;
+
+    ///
+    /// \brief Get compress-mode.
+    /// \return bool
+    inline bool compress() const;
+
+    ///
+    /// \brief Get compression level.
+    /// \return [0..9]
+    inline unsigned getCompressionLevel() const;
+
+    ///
+    /// \brief Get chunking mode: ``true`` is manually set, ``false`` if chunk-size should be
+    /// computed automatically.
+    /// \return bool
+    inline bool isChunked() const;
+
+    ///
+    /// \brief Get chunk size. Use DumpOptions::getChunkSize to check if chunk-size should
+    /// be automatically computed.
+    inline std::vector<hsize_t> getChunkSize() const;
+
+private:
+    bool m_overwrite = false;
+    bool m_flush = true;
+    unsigned m_compression_level = 0;
+    std::vector<hsize_t> m_chunk_size = {};
+};
+
+///
+/// \brief Get the size of an existing DataSet in an open HDF5 file.
+///
+/// \param file opened file (has to be readable)
+/// \param path path of the DataSet
+///
+/// \return Size of the DataSet
+inline size_t getSize(const File& file, const std::string& path);
+
+///
+/// \brief Get the shape of an existing DataSet in an readable file.
+///
+/// \param file opened file (has to be readable)
+/// \param path Path of the DataSet
+///
+/// \return the shape of the DataSet
+inline std::vector<size_t> getShape(const File& file, const std::string& path);
+
+///
+/// \brief Write object (templated) to a (new) DataSet in an open HDF5 file.
+///
+/// \param file opened file (has to be writeable)
+/// \param path path of the DataSet
+/// \param data the data to write (any supported type)
+/// \param mode write mode
+///
+/// \return The newly created DataSet
+///
+template <class T>
+inline DataSet dump(File& file,
+                    const std::string& path,
+                    const T& data,
+                    DumpMode mode = DumpMode::Create);
+
+///
+/// \brief Write object (templated) to a (new) DataSet in an open HDF5 file.
+///
+/// \param file opened file (has to be writeable)
+/// \param path path of the DataSet
+/// \param data the data to write (any supported type)
+/// \param options dump options
+///
+/// \return The newly created DataSet
+///
+template <class T>
+inline DataSet dump(File& file,
+                    const std::string& path,
+                    const T& data,
+                    const DumpOptions& options);
+
+///
+/// \brief Write a scalar to a (new, extendible) DataSet in an open HDF5 file.
+///
+/// \param file opened file (has to be writeable)
+/// \param path path of the DataSet
+/// \param data the data to write (any supported type)
+/// \param idx the indices to which to write
+///
+/// \return The newly created DataSet
+///
+template <class T>
+inline DataSet dump(File& file,
+                    const std::string& path,
+                    const T& data,
+                    const std::vector<size_t>& idx);
+
+///
+/// \brief Write a scalar to a (new, extendable) DataSet in an open HDF5 file.
+///
+/// \param file open File (has to be writeable)
+/// \param path path of the DataSet
+/// \param data the data to write (any supported type)
+/// \param idx the indices to which to write
+///
+/// \return The newly created DataSet
+///
+template <class T>
+inline DataSet dump(File& file,
+                    const std::string& path,
+                    const T& data,
+                    const std::initializer_list<size_t>& idx);
+
+///
+/// \brief Write a scalar to a (new, extendible) DataSet in an open HDF5 file.
+///
+/// \param file opened file (has to be writeable)
+/// \param path path of the DataSet
+/// \param data the data to write (any supported type)
+/// \param idx the indices to which to write
+/// \param options dump options
+///
+/// \return The newly created DataSet
+///
+template <class T>
+inline DataSet dump(File& file,
+                    const std::string& path,
+                    const T& data,
+                    const std::vector<size_t>& idx,
+                    const DumpOptions& options);
+
+///
+/// \brief Write a scalar to a (new, extendible) DataSet in an open HDF5 file.
+///
+/// \param file opened file (has to be writeable)
+/// \param path path of the DataSet
+/// \param data the data to write (any supported type)
+/// \param idx the indices to which to write
+/// \param options dump options
+///
+/// \return The newly created DataSet
+///
+template <class T>
+inline DataSet dump(File& file,
+                    const std::string& path,
+                    const T& data,
+                    const std::initializer_list<size_t>& idx,
+                    const DumpOptions& options);
+
+///
+/// \brief Load entry ``{i, j, ...}`` from a DataSet in an open HDF5 file to a scalar.
+///
+/// \param file opened file (has to be writeable)
+/// \param idx the indices to load
+/// \param path path of the DataSet
+///
+/// \return The read data
+///
+template <class T>
+inline T load(const File& file, const std::string& path, const std::vector<size_t>& idx);
+
+///
+/// \brief Load a DataSet in an open HDF5 file to an object (templated).
+///
+/// \param file opened file (has to be writeable)
+/// \param path path of the DataSet
+///
+/// \return The read data
+///
+template <class T>
+inline T load(const File& file, const std::string& path);
+
+///
+/// \brief Write object (templated) to a (new) Attribute in an open HDF5 file.
+///
+/// \param file opened file (has to be writeable)
+/// \param path path of the DataSet
+/// \param key name of the attribute
+/// \param data the data to write (any supported type)
+/// \param mode write mode
+///
+/// \return The newly created DataSet
+///
+template <class T>
+inline Attribute dumpAttribute(File& file,
+                               const std::string& path,
+                               const std::string& key,
+                               const T& data,
+                               DumpMode mode = DumpMode::Create);
+
+///
+/// \brief Write object (templated) to a (new) Attribute in an open HDF5 file.
+///
+/// \param file opened file (has to be writeable)
+/// \param path path of the DataSet
+/// \param key name of the attribute
+/// \param data the data to write (any supported type)
+/// \param options dump options
+///
+/// \return The newly created DataSet
+///
+template <class T>
+inline Attribute dumpAttribute(File& file,
+                               const std::string& path,
+                               const std::string& key,
+                               const T& data,
+                               const DumpOptions& options);
+
+///
+/// \brief Load a Attribute in an open HDF5 file to an object (templated).
+///
+/// \param file opened file (has to be writeable)
+/// \param path path of the DataSet
+/// \param key name of the attribute
+///
+/// \return The read data
+///
+template <class T>
+inline T loadAttribute(const File& file, const std::string& path, const std::string& key);
+
+}  // namespace H5Easy
+
+#include "h5easy_bits/H5Easy_Eigen.hpp"
+#include "h5easy_bits/H5Easy_misc.hpp"
+#include "h5easy_bits/H5Easy_opencv.hpp"
+#include "h5easy_bits/H5Easy_public.hpp"
+#include "h5easy_bits/H5Easy_scalar.hpp"
+#include "h5easy_bits/H5Easy_vector.hpp"
+#include "h5easy_bits/H5Easy_xtensor.hpp"
+
+#endif  // H5EASY_HPP
diff --git a/vendor/highfive/include/highfive/H5Exception.hpp b/vendor/highfive/include/highfive/H5Exception.hpp
new file mode 100644
index 00000000..4ca805bd
--- /dev/null
+++ b/vendor/highfive/include/highfive/H5Exception.hpp
@@ -0,0 +1,147 @@
+/*
+ *  Copyright (c), 2017, Adrien Devresse <adrien.devresse@epfl.ch>
+ *
+ *  Distributed under the Boost Software License, Version 1.0.
+ *    (See accompanying file LICENSE_1_0.txt or copy at
+ *          http://www.boost.org/LICENSE_1_0.txt)
+ *
+ */
+#ifndef H5EXCEPTION_HPP
+#define H5EXCEPTION_HPP
+
+#include <memory>
+#include <stdexcept>
+#include <string>
+
+#include <H5Ipublic.h>
+
+namespace HighFive {
+
+///
+/// \brief Basic HighFive Exception class
+///
+///
+class Exception : public std::exception {
+  public:
+    Exception(const std::string& err_msg)
+        : _errmsg(err_msg), _next(), _err_major(0), _err_minor(0) {}
+
+    virtual ~Exception() throw() {}
+
+    ///
+    /// \brief get the current exception error message
+    /// \return
+    ///
+    inline const char* what() const throw() override { return _errmsg.c_str(); }
+
+    ///
+    /// \brief define the error message
+    /// \param errmsg
+    ///
+    inline virtual void setErrorMsg(const std::string& errmsg) {
+        _errmsg = errmsg;
+    }
+
+    ///
+    /// \brief nextException
+    /// \return pointer to the next exception in the chain, or NULL if not
+    /// existing
+    ///
+    inline Exception* nextException() const { return _next.get(); }
+
+    ///
+    /// \brief HDF5 library error mapper
+    /// \return HDF5 major error number
+    ///
+    inline hid_t getErrMajor() const { return _err_major; }
+
+    ///
+    /// \brief HDF5 library error mapper
+    /// \return HDF5 minor error number
+    ///
+    inline hid_t getErrMinor() const { return _err_minor; }
+
+  protected:
+    std::string _errmsg;
+    std::shared_ptr<Exception> _next;
+    hid_t _err_major, _err_minor;
+
+    friend struct HDF5ErrMapper;
+};
+
+///
+/// \brief Exception specific to HighFive Object interface
+///
+class ObjectException : public Exception {
+  public:
+    ObjectException(const std::string& err_msg) : Exception(err_msg) {}
+};
+
+///
+/// \brief Exception specific to HighFive DataType interface
+///
+class DataTypeException : public Exception {
+  public:
+    DataTypeException(const std::string& err_msg) : Exception(err_msg) {}
+};
+
+///
+/// \brief Exception specific to HighFive File interface
+///
+class FileException : public Exception {
+  public:
+    FileException(const std::string& err_msg) : Exception(err_msg) {}
+};
+
+///
+/// \brief Exception specific to HighFive DataSpace interface
+///
+class DataSpaceException : public Exception {
+  public:
+    DataSpaceException(const std::string& err_msg) : Exception(err_msg) {}
+};
+
+///
+/// \brief Exception specific to HighFive Attribute interface
+///
+class AttributeException : public Exception {
+  public:
+    AttributeException(const std::string& err_msg) : Exception(err_msg) {}
+};
+
+///
+/// \brief Exception specific to HighFive DataSet interface
+///
+class DataSetException : public Exception {
+  public:
+    DataSetException(const std::string& err_msg) : Exception(err_msg) {}
+};
+
+///
+/// \brief Exception specific to HighFive Group interface
+///
+class GroupException : public Exception {
+  public:
+    GroupException(const std::string& err_msg) : Exception(err_msg) {}
+};
+
+///
+/// \brief Exception specific to HighFive Property interface
+///
+class PropertyException : public Exception {
+  public:
+    PropertyException(const std::string& err_msg) : Exception(err_msg) {}
+};
+
+///
+/// \brief Exception specific to HighFive Reference interface
+///
+class ReferenceException : public Exception {
+  public:
+    ReferenceException(const std::string& err_msg) : Exception(err_msg) {}
+};
+}
+
+#include "bits/H5Exception_misc.hpp"
+
+#endif // H5EXCEPTION_HPP
diff --git a/vendor/highfive/include/highfive/H5File.hpp b/vendor/highfive/include/highfive/H5File.hpp
new file mode 100644
index 00000000..9768c34b
--- /dev/null
+++ b/vendor/highfive/include/highfive/H5File.hpp
@@ -0,0 +1,94 @@
+/*
+ *  Copyright (c), 2017, Adrien Devresse <adrien.devresse@epfl.ch>
+ *
+ *  Distributed under the Boost Software License, Version 1.0.
+ *    (See accompanying file LICENSE_1_0.txt or copy at
+ *          http://www.boost.org/LICENSE_1_0.txt)
+ *
+ */
+#ifndef H5FILE_HPP
+#define H5FILE_HPP
+
+#include <string>
+
+#include "H5FileDriver.hpp"
+#include "H5Object.hpp"
+#include "bits/H5Annotate_traits.hpp"
+#include "bits/H5Node_traits.hpp"
+
+namespace HighFive {
+
+///
+/// \brief File class
+///
+class File : public Object,
+             public NodeTraits<File>,
+             public AnnotateTraits<File> {
+ public:
+
+    const static ObjectType type = ObjectType::File;
+
+    enum : unsigned {
+        /// Open flag: Read only access
+        ReadOnly = 0x00u,
+        /// Open flag: Read Write access
+        ReadWrite = 0x01u,
+        /// Open flag: Truncate a file if already existing
+        Truncate = 0x02u,
+        /// Open flag: Open will fail if file already exist
+        Excl = 0x04u,
+        /// Open flag: Open in debug mode
+        Debug = 0x08u,
+        /// Open flag: Create non existing file
+        Create = 0x10u,
+        /// Derived open flag: common write mode (=ReadWrite|Create|Truncate)
+        Overwrite = Truncate,
+        /// Derived open flag: Opens RW or exclusively creates
+        OpenOrCreate = ReadWrite | Create
+    };
+
+    ///
+    /// \brief File
+    /// \param filename: filepath of the HDF5 file
+    /// \param openFlags: Open mode / flags ( ReadOnly, ReadWrite)
+    /// \param fileAccessProps: the file access properties
+    ///
+    /// Open or create a new HDF5 file
+    explicit File(const std::string& filename, unsigned openFlags = ReadOnly,
+                  const FileAccessProps& fileAccessProps = FileAccessProps::Default());
+
+    ///
+    /// \brief Return the name of the file
+    ///
+    const std::string& getName() const noexcept;
+
+
+    /// \brief Object path of a File is always "/"
+    std::string getPath() const noexcept {
+        return "/";
+    }
+
+    ///
+    /// \brief flush
+    ///
+    /// Flushes all buffers associated with a file to disk
+    ///
+    void flush();
+
+ private:
+    using Object::Object;
+
+    mutable std::string _filename{};
+
+    template <typename> friend class PathTraits;
+};
+
+}  // namespace HighFive
+
+// H5File is the main user constructible -> bring in implementation headers
+#include "bits/H5Annotate_traits_misc.hpp"
+#include "bits/H5File_misc.hpp"
+#include "bits/H5Node_traits_misc.hpp"
+#include "bits/H5Path_traits_misc.hpp"
+
+#endif  // H5FILE_HPP
diff --git a/vendor/highfive/include/highfive/H5FileDriver.hpp b/vendor/highfive/include/highfive/H5FileDriver.hpp
new file mode 100644
index 00000000..b18c4f3e
--- /dev/null
+++ b/vendor/highfive/include/highfive/H5FileDriver.hpp
@@ -0,0 +1,36 @@
+/*
+ *  Copyright (c), 2017, Adrien Devresse <adrien.devresse@epfl.ch>
+ *
+ *  Distributed under the Boost Software License, Version 1.0.
+ *    (See accompanying file LICENSE_1_0.txt or copy at
+ *          http://www.boost.org/LICENSE_1_0.txt)
+ *
+ */
+#ifndef H5FILEDRIVER_HPP
+#define H5FILEDRIVER_HPP
+
+#include "H5PropertyList.hpp"
+
+namespace HighFive {
+
+///
+/// \brief file driver base concept
+///
+class FileDriver : public FileAccessProps {};
+
+///
+/// \brief MPIIO Driver for Parallel HDF5
+///
+class MPIOFileDriver : public FileAccessProps {
+  public:
+    template <typename Comm, typename Info>
+    inline MPIOFileDriver(Comm mpi_comm, Info mpi_info);
+
+  private:
+};
+
+}  // namespace HighFive
+
+#include "bits/H5FileDriver_misc.hpp"
+
+#endif // H5FILEDRIVER_HPP
diff --git a/vendor/highfive/include/highfive/H5Group.hpp b/vendor/highfive/include/highfive/H5Group.hpp
new file mode 100644
index 00000000..e8bc3e43
--- /dev/null
+++ b/vendor/highfive/include/highfive/H5Group.hpp
@@ -0,0 +1,44 @@
+/*
+ *  Copyright (c), 2017, Adrien Devresse <adrien.devresse@epfl.ch>
+ *
+ *  Distributed under the Boost Software License, Version 1.0.
+ *    (See accompanying file LICENSE_1_0.txt or copy at
+ *          http://www.boost.org/LICENSE_1_0.txt)
+ *
+ */
+#ifndef HIGHFIVE_H5GROUP_HPP
+#define HIGHFIVE_H5GROUP_HPP
+
+#include "H5Object.hpp"
+#include "bits/H5_definitions.hpp"
+#include "bits/H5Annotate_traits.hpp"
+#include "bits/H5Node_traits.hpp"
+#include "bits/H5Path_traits.hpp"
+
+namespace HighFive {
+
+///
+/// \brief Represents an hdf5 group
+class Group : public Object,
+              public NodeTraits<Group>,
+              public AnnotateTraits<Group>,
+              public PathTraits<Group> {
+  public:
+    const static ObjectType type = ObjectType::Group;
+
+    H5_DEPRECATED("Default constructor creates unsafe uninitialized objects")
+    Group() = default;
+
+  protected:
+    using Object::Object;
+
+    Group(Object&& o) noexcept : Object(std::move(o)) {};
+
+    friend class File;
+    friend class Reference;
+    template <typename Derivate> friend class ::HighFive::NodeTraits;
+};
+
+}  // namespace HighFive
+
+#endif // HIGHFIVE_H5GROUP_HPP
diff --git a/vendor/highfive/include/highfive/H5Object.hpp b/vendor/highfive/include/highfive/H5Object.hpp
new file mode 100644
index 00000000..e4bfd365
--- /dev/null
+++ b/vendor/highfive/include/highfive/H5Object.hpp
@@ -0,0 +1,131 @@
+/*
+ *  Copyright (c), 2017, Adrien Devresse <adrien.devresse@epfl.ch>
+ *
+ *  Distributed under the Boost Software License, Version 1.0.
+ *    (See accompanying file LICENSE_1_0.txt or copy at
+ *          http://www.boost.org/LICENSE_1_0.txt)
+ *
+ */
+#ifndef H5OBJECT_HPP
+#define H5OBJECT_HPP
+
+#include <ctime>
+
+#include <H5Ipublic.h>
+#include <H5Opublic.h>
+
+#include "H5Exception.hpp"
+#include "bits/H5_definitions.hpp"
+
+namespace HighFive {
+
+///
+/// \brief Enum of the types of objects (H5O api)
+///
+enum class ObjectType {
+    File,
+    Group,
+    UserDataType,
+    DataSpace,
+    Dataset,
+    Attribute,
+    Other  // Internal/custom object type
+};
+
+
+class Object {
+  public:
+    // decrease reference counter
+    ~Object();
+
+    ///
+    /// \brief isValid
+    /// \return true if current Object is a valid HDF5Object
+    ///
+    bool isValid() const noexcept;
+
+    ///
+    /// \brief getId
+    /// \return internal HDF5 id to the object
+    ///  provided for C API compatibility
+    ///
+    hid_t getId() const noexcept;
+
+    ///
+    /// \brief Retrieve several infos about the current object (address, dates, etc)
+    ///
+    ObjectInfo getInfo() const;
+
+    ///
+    /// \brief Gets the fundamental type of the object (dataset, group, etc)
+    /// \exception ObjectException when the _hid is negative or the type
+    ///     is custom and not registered yet
+    ///
+    ObjectType getType() const;
+
+    // Check if refer to same object
+    bool operator==(const Object& other) const noexcept {
+        return _hid == other._hid;
+    }
+
+  protected:
+    // empty constructor
+    Object();
+
+    // copy constructor, increase reference counter
+    Object(const Object& other);
+
+    // move constructor, reuse hid
+    Object(Object&& other) noexcept;
+
+    // Init with an low-level object id
+    explicit Object(hid_t);
+
+    // Copy-Assignment operator
+    Object& operator=(const Object& other);
+
+    hid_t _hid;
+
+  private:
+
+    template <typename Derivate> friend class NodeTraits;
+    template <typename Derivate> friend class AnnotateTraits;
+    friend class Reference;
+};
+
+
+///
+/// \brief A class for accessing hdf5 objects info
+///
+class ObjectInfo  {
+  public:
+    /// \brief Retrieve the address of the object (within its file)
+    H5_DEPRECATED("Deprecated since HighFive 2.2. Soon supporting VOL tokens")
+    haddr_t getAddress() const noexcept;
+
+    /// \brief Retrieve the number of references to this object
+    size_t getRefCount() const noexcept;
+
+    /// \brief Retrieve the object's creation time
+    time_t getCreationTime() const noexcept;
+
+    /// \brief Retrieve the object's last modification time
+    time_t getModificationTime() const noexcept;
+
+  protected:
+
+#if (H5Oget_info_vers < 3)
+    H5O_info_t raw_info;
+#else
+    // Use compat H5O_info1_t while getAddress() is supported (deprecated)
+    H5O_info1_t raw_info;
+#endif
+
+    friend class Object;
+};
+
+}  // namespace HighFive
+
+#include "bits/H5Object_misc.hpp"
+
+#endif // H5OBJECT_HPP
diff --git a/vendor/highfive/include/highfive/H5PropertyList.hpp b/vendor/highfive/include/highfive/H5PropertyList.hpp
new file mode 100644
index 00000000..538b4e5b
--- /dev/null
+++ b/vendor/highfive/include/highfive/H5PropertyList.hpp
@@ -0,0 +1,213 @@
+/*
+ *  Copyright (c), 2017-2018, Adrien Devresse <adrien.devresse@epfl.ch>
+ *                            Juan Hernando <juan.hernando@epfl.ch>
+ *  Distributed under the Boost Software License, Version 1.0.
+ *    (See accompanying file LICENSE_1_0.txt or copy at
+ *          http://www.boost.org/LICENSE_1_0.txt)
+ *
+ */
+#ifndef H5PROPERTY_LIST_HPP
+#define H5PROPERTY_LIST_HPP
+
+#include <vector>
+
+#include <H5Ppublic.h>
+
+#include "H5Exception.hpp"
+#include "H5Object.hpp"
+
+namespace HighFive {
+
+///
+/// \brief Types of property lists
+///
+enum class PropertyType : int {
+    OBJECT_CREATE,
+    FILE_CREATE,
+    FILE_ACCESS,
+    DATASET_CREATE,
+    DATASET_ACCESS,
+    DATASET_XFER,
+    GROUP_CREATE,
+    GROUP_ACCESS,
+    DATATYPE_CREATE,
+    DATATYPE_ACCESS,
+    STRING_CREATE,
+    ATTRIBUTE_CREATE,
+    OBJECT_COPY,
+    LINK_CREATE,
+    LINK_ACCESS,
+};
+
+
+///
+/// \brief Base Class for Property lists, providing global default
+class PropertyListBase : public Object {
+
+  public:
+    PropertyListBase() noexcept;
+
+    static const PropertyListBase& Default() noexcept {
+        static const PropertyListBase plist{};
+        return plist;
+    }
+
+};
+
+
+///
+/// \brief HDF5 property Lists
+///
+template <PropertyType T>
+class PropertyList : public PropertyListBase {
+  public:
+
+    ///
+    /// \brief return the type of this PropertyList
+    constexpr PropertyType getType() const noexcept {
+        return T;
+    }
+
+    ///
+    /// Add a property to this property list.
+    /// A property is an object which is expected to have a method with the
+    /// following signature void apply(hid_t hid) const
+    ///
+    template <typename P>
+    void add(const P& property);
+
+    ///
+    /// Return the Default property type object
+    static const PropertyList<T>& Default() noexcept {
+        return static_cast<const PropertyList<T>&>(PropertyListBase::Default());
+    }
+
+  protected:
+    void _initializeIfNeeded();
+
+};
+
+typedef PropertyList<PropertyType::OBJECT_CREATE> ObjectCreateProps;
+typedef PropertyList<PropertyType::FILE_CREATE> FileCreateProps;
+typedef PropertyList<PropertyType::FILE_ACCESS> FileAccessProps ;
+typedef PropertyList<PropertyType::DATASET_CREATE> DataSetCreateProps;
+typedef PropertyList<PropertyType::DATASET_ACCESS> DataSetAccessProps;
+typedef PropertyList<PropertyType::DATASET_XFER> DataTransferProps;
+typedef PropertyList<PropertyType::GROUP_CREATE> GroupCreateProps;
+typedef PropertyList<PropertyType::GROUP_ACCESS> GroupAccessProps;
+typedef PropertyList<PropertyType::DATATYPE_CREATE> DataTypeCreateProps;
+typedef PropertyList<PropertyType::DATATYPE_ACCESS> DataTypeAccessProps;
+typedef PropertyList<PropertyType::STRING_CREATE> StringCreateProps;
+typedef PropertyList<PropertyType::ATTRIBUTE_CREATE> AttributeCreateProps;
+typedef PropertyList<PropertyType::OBJECT_COPY> ObjectCopyProps;
+typedef PropertyList<PropertyType::LINK_CREATE> LinkCreateProps;
+typedef PropertyList<PropertyType::LINK_ACCESS> LinkAccessProps;
+
+///
+/// RawPropertieLists are to be used when advanced H5 properties
+/// are desired and are not part of the HighFive API.
+/// Therefore this class is mainly for internal use.
+template <PropertyType T>
+class RawPropertyList : public PropertyList<T> {
+  public:
+    template <typename F, typename... Args>
+    void add(const F& funct, const Args&... args);
+};
+
+
+class Chunking {
+  public:
+    explicit Chunking(const std::vector<hsize_t>& dims)
+        : _dims(dims) {}
+
+    Chunking(const std::initializer_list<hsize_t>& items)
+        : Chunking(std::vector<hsize_t>{items}) {}
+
+    template <typename... Args>
+    explicit Chunking(hsize_t item, Args... args)
+        : Chunking(std::vector<hsize_t>{item, static_cast<hsize_t>(args)...}) {}
+
+    const std::vector<hsize_t>& getDimensions() const noexcept {
+        return _dims;
+    }
+
+  private:
+    friend DataSetCreateProps;
+    void apply(hid_t hid) const;
+    const std::vector<hsize_t> _dims;
+};
+
+class Deflate {
+  public:
+    explicit Deflate(unsigned level)
+        : _level(level) {}
+
+  private:
+    friend DataSetCreateProps;
+    void apply(hid_t hid) const;
+    const unsigned _level;
+};
+
+class Szip {
+  public:
+    explicit Szip(unsigned options_mask = H5_SZIP_EC_OPTION_MASK, 
+                  unsigned pixels_per_block = H5_SZIP_MAX_PIXELS_PER_BLOCK)
+        : _options_mask(options_mask)
+        , _pixels_per_block(pixels_per_block)
+    {}
+
+    private:
+      friend DataSetCreateProps;
+      void apply(hid_t hid) const;
+      const unsigned _options_mask;
+      const unsigned _pixels_per_block;
+};
+
+class Shuffle {
+  public:
+    Shuffle() = default;
+
+  private:
+    friend DataSetCreateProps;
+    void apply(hid_t hid) const;
+};
+
+/// Dataset access property to control chunk cache configuration.
+/// Do not confuse with the similar file access property for H5Pset_cache
+class Caching {
+  public:
+    /// https://support.hdfgroup.org/HDF5/doc/RM/H5P/H5Pset_chunk_cache.html for
+    /// details.
+    Caching(const size_t numSlots,
+            const size_t cacheSize,
+            const double w0 = static_cast<double>(H5D_CHUNK_CACHE_W0_DEFAULT))
+        : _numSlots(numSlots)
+        , _cacheSize(cacheSize)
+        , _w0(w0) {}
+
+  private:
+    friend DataSetAccessProps;
+    void apply(hid_t hid) const;
+    const size_t _numSlots;
+    const size_t _cacheSize;
+    const double _w0;
+};
+
+class CreateIntermediateGroup {
+  public:
+    explicit CreateIntermediateGroup(bool create=true)
+        : _create(create)
+    {}
+
+  private:
+    friend ObjectCreateProps;
+    friend LinkCreateProps;
+    void apply(hid_t hid) const;
+    const bool _create;
+};
+
+}  // namespace HighFive
+
+#include "bits/H5PropertyList_misc.hpp"
+
+#endif  // H5PROPERTY_LIST_HPP
diff --git a/vendor/highfive/include/highfive/H5Reference.hpp b/vendor/highfive/include/highfive/H5Reference.hpp
new file mode 100644
index 00000000..2c8ddcb7
--- /dev/null
+++ b/vendor/highfive/include/highfive/H5Reference.hpp
@@ -0,0 +1,82 @@
+/*
+ *  Copyright (c), 2020, EPFL - Blue Brain Project
+ *
+ *  Distributed under the Boost Software License, Version 1.0.
+ *    (See accompanying file LICENSE_1_0.txt or copy at
+ *          http://www.boost.org/LICENSE_1_0.txt)
+ *
+ */
+
+#ifndef H5REFERENCE_HPP
+#define H5REFERENCE_HPP
+
+#include <string>
+#include <vector>
+
+#include <H5Ipublic.h>
+#include <H5Rpublic.h>
+
+#include "H5Object.hpp"
+#include "bits/H5_definitions.hpp"
+
+namespace HighFive {
+
+///
+/// \brief An HDF5 (object) reference type
+///
+/// HDF5 object references allow pointing to groups, datasets (and compound types). They
+/// differ from links in their ability to be stored and retrieved as data from the HDF5
+/// file in datasets themselves.
+///
+class Reference {
+  public:
+    /// \brief Create an empty Reference to be initialized later
+    Reference() = default;
+
+    /// \brief Create a Reference to an object residing at a given location
+    ///
+    /// \param location A File or Group where the object being referenced to resides
+    /// \param object A Dataset or Group to be referenced
+    Reference(const Object& location, const Object& object);
+
+    /// \brief Retrieve the Object being referenced by the Reference
+    ///
+    /// \tparam T the appropriate HighFive Container (either DataSet or Group)
+    /// \param location the location where the referenced object is to be found (a File)
+    /// \return the dereferenced Object (either a Group or DataSet)
+    template <typename T>
+    T dereference(const Object& location) const;
+
+    /// \brief Get only the type of the referenced Object
+    ///
+    /// \param location the location where the referenced object is to be found (a File)
+    /// \return the ObjectType of the referenced object
+    ObjectType getType(const Object& location) const;
+
+  protected:
+    /// \brief Create a Reference from a low-level HDF5 object reference
+    inline explicit Reference(const hobj_ref_t h5_ref)
+        : href(h5_ref) {};
+
+    /// \brief Create the low-level reference and store it at refptr
+    ///
+    /// \param refptr Pointer to a memory location where the created HDF5 reference will
+    /// be stored
+    void create_ref(hobj_ref_t* refptr) const;
+
+  private:
+
+    Object get_ref(const Object& location) const;
+
+    hobj_ref_t href{};
+    std::string obj_name{};
+    hid_t parent_id{};
+
+    friend details::data_converter<std::vector<Reference>>;
+};
+
+}  // namespace HighFive
+
+#include "bits/H5Reference_misc.hpp"
+
+#endif  // H5REFERENCE_HPP
diff --git a/vendor/highfive/include/highfive/H5Selection.hpp b/vendor/highfive/include/highfive/H5Selection.hpp
new file mode 100644
index 00000000..c367e42a
--- /dev/null
+++ b/vendor/highfive/include/highfive/H5Selection.hpp
@@ -0,0 +1,64 @@
+/*
+ *  Copyright (c), 2017, Adrien Devresse <adrien.devresse@epfl.ch>
+ *
+ *  Distributed under the Boost Software License, Version 1.0.
+ *    (See accompanying file LICENSE_1_0.txt or copy at
+ *          http://www.boost.org/LICENSE_1_0.txt)
+ *
+ */
+#ifndef H5SELECTION_HPP
+#define H5SELECTION_HPP
+
+#include "H5DataSet.hpp"
+#include "H5DataSpace.hpp"
+#include "bits/H5Slice_traits.hpp"
+
+namespace HighFive {
+
+///
+/// \brief Selection: represent a view on a slice/part of a dataset
+///
+/// A Selection is valid only if its parent dataset is valid
+///
+class Selection : public SliceTraits<Selection> {
+  public:
+    ///
+    /// \brief getSpace
+    /// \return Dataspace associated with this selection
+    ///
+    DataSpace getSpace() const noexcept;
+
+    ///
+    /// \brief getMemSpace
+    /// \return Dataspace associated with the memory representation of this
+    /// selection
+    ///
+    DataSpace getMemSpace() const noexcept;
+
+    ///
+    /// \brief getDataSet
+    /// \return parent dataset of this selection
+    ///
+    DataSet& getDataset() noexcept;
+    const DataSet& getDataset() const noexcept;
+
+    ///
+    /// \brief return the datatype of the selection
+    /// \return return the datatype of the selection
+    const DataType getDataType() const;
+
+  private:
+    Selection(const DataSpace& memspace,
+              const DataSpace& file_space,
+              const DataSet& set);
+
+    DataSpace _mem_space, _file_space;
+    DataSet _set;
+
+    template <typename Derivate> friend class ::HighFive::SliceTraits;
+    // absolute namespace naming due to GCC bug 52625
+};
+
+}  // namespace HighFive
+
+#endif // H5SELECTION_HPP
diff --git a/vendor/highfive/include/highfive/H5Utility.hpp b/vendor/highfive/include/highfive/H5Utility.hpp
new file mode 100644
index 00000000..b8fa2f0a
--- /dev/null
+++ b/vendor/highfive/include/highfive/H5Utility.hpp
@@ -0,0 +1,40 @@
+/*
+ *  Copyright (c), 2017, Blue Brain Project - EPFL (CH)
+ *
+ *  Distributed under the Boost Software License, Version 1.0.
+ *    (See accompanying file LICENSE_1_0.txt or copy at
+ *          http://www.boost.org/LICENSE_1_0.txt)
+ *
+ */
+
+#ifndef H5UTILITY_HPP
+#define H5UTILITY_HPP
+
+#include <H5Epublic.h>
+
+namespace HighFive {
+
+///
+/// \brief Utility class to disable HDF5 stack printing inside a scope.
+///
+class SilenceHDF5 {
+public:
+    inline SilenceHDF5(bool enable=true)
+        : _client_data(nullptr)
+    {
+        H5Eget_auto2(H5E_DEFAULT, &_func, &_client_data);
+        if (enable) H5Eset_auto2(H5E_DEFAULT, NULL, NULL);
+    }
+
+    inline ~SilenceHDF5() {
+        H5Eset_auto2(H5E_DEFAULT, _func, _client_data);
+    }
+
+private:
+    H5E_auto2_t _func;
+    void* _client_data;
+};
+
+}  // namespace HighFive
+
+#endif // H5UTIL_HPP
diff --git a/vendor/highfive/include/highfive/H5Version.hpp.in b/vendor/highfive/include/highfive/H5Version.hpp.in
new file mode 100644
index 00000000..5969ebc9
--- /dev/null
+++ b/vendor/highfive/include/highfive/H5Version.hpp.in
@@ -0,0 +1,17 @@
+/*
+ *  Copyright (c), 2020
+ *
+ *  Distributed under the Boost Software License, Version 1.0.
+ *    (See accompanying file LICENSE_1_0.txt or copy at
+ *          http://www.boost.org/LICENSE_1_0.txt)
+ *
+ */
+#ifndef H5VERSION_HPP
+#define H5VERSION_HPP
+
+#define HIGHFIVE_VERSION_MAJOR @PROJECT_VERSION_MAJOR@
+#define HIGHFIVE_VERSION_MINOR @PROJECT_VERSION_MINOR@
+#define HIGHFIVE_VERSION_PATCH @PROJECT_VERSION_PATCH@
+#define HIGHFIVE_VERSION @PROJECT_VERSION@
+
+#endif // H5VERSION_HPP
diff --git a/vendor/highfive/include/highfive/bits/H5Annotate_traits.hpp b/vendor/highfive/include/highfive/bits/H5Annotate_traits.hpp
new file mode 100644
index 00000000..757ae0d0
--- /dev/null
+++ b/vendor/highfive/include/highfive/bits/H5Annotate_traits.hpp
@@ -0,0 +1,87 @@
+/*
+ *  Copyright (c), 2017, Adrien Devresse <adrien.devresse@epfl.ch>
+ *
+ *  Distributed under the Boost Software License, Version 1.0.
+ *    (See accompanying file LICENSE_1_0.txt or copy at
+ *          http://www.boost.org/LICENSE_1_0.txt)
+ *
+ */
+#ifndef H5ANNOTATE_TRAITS_HPP
+#define H5ANNOTATE_TRAITS_HPP
+
+#include <string>
+
+#include "../H5Attribute.hpp"
+
+namespace HighFive {
+
+template <typename Derivate>
+class AnnotateTraits {
+  public:
+    ///
+    /// \brief create a new attribute with the name attribute_name
+    /// \param attribute_name identifier of the attribute
+    /// \param space Associated \ref DataSpace
+    /// \param type
+    /// \return the attribute object
+    ///
+    Attribute createAttribute(const std::string& attribute_name,
+                              const DataSpace& space,
+                              const DataType& type);
+
+    ///
+    /// \brief createAttribute create a new attribute on the current dataset with
+    /// size specified by space
+    /// \param attribute_name identifier of the attribute
+    /// \param space Associated DataSpace
+    /// informations
+    /// \return Attribute Object
+    template <typename Type>
+    Attribute createAttribute(const std::string& attribute_name,
+                              const DataSpace& space);
+
+    ///
+    /// \brief createAttribute create a new attribute on the current dataset and
+    /// write to it, inferring the DataSpace from data.
+    /// \param attribute_name identifier of the attribute
+    /// \param data Associated data to write, must support DataSpace::From, see
+    /// \ref DataSpace for more information
+    /// \return Attribute Object
+    ///
+    template <typename T>
+    Attribute createAttribute(const std::string& attribute_name,
+                              const T& data);
+
+    ///
+    /// \brief deleteAttribute let you delete an attribute by its name.
+    /// \param attribute_name identifier of the attribute
+    void deleteAttribute(const std::string& attribute_name);
+
+    ///
+    /// \brief open an existing attribute with the name attribute_name
+    /// \param attribute_name identifier of the attribute
+    /// \return the attribute object
+    Attribute getAttribute(const std::string& attribute_name) const;
+
+    ///
+    /// \brief return the number of attributes of the node / group
+    /// \return number of attributes
+    size_t getNumberAttributes() const;
+
+    ///
+    /// \brief list all attribute name of the node / group
+    /// \return number of attributes
+    std::vector<std::string> listAttributeNames() const;
+
+    ///
+    /// \brief checks an attribute exists
+    /// \return number of attributes
+    bool hasAttribute(const std::string& attr_name) const;
+
+  private:
+    typedef Derivate derivate_type;
+};
+}
+
+
+#endif // H5ANNOTATE_TRAITS_HPP
diff --git a/vendor/highfive/include/highfive/bits/H5Annotate_traits_misc.hpp b/vendor/highfive/include/highfive/bits/H5Annotate_traits_misc.hpp
new file mode 100644
index 00000000..5013b347
--- /dev/null
+++ b/vendor/highfive/include/highfive/bits/H5Annotate_traits_misc.hpp
@@ -0,0 +1,125 @@
+/*
+ *  Copyright (c), 2017, Adrien Devresse <adrien.devresse@epfl.ch>
+ *
+ *  Distributed under the Boost Software License, Version 1.0.
+ *    (See accompanying file LICENSE_1_0.txt or copy at
+ *          http://www.boost.org/LICENSE_1_0.txt)
+ *
+ */
+#ifndef H5ANNOTATE_TRAITS_MISC_HPP
+#define H5ANNOTATE_TRAITS_MISC_HPP
+
+#include <string>
+#include <vector>
+
+#include <H5Apublic.h>
+#include <H5Ppublic.h>
+
+#include "H5Attribute_misc.hpp"
+#include "H5Iterables_misc.hpp"
+
+namespace HighFive {
+
+template <typename Derivate>
+inline Attribute
+AnnotateTraits<Derivate>::createAttribute(const std::string& attribute_name,
+                                          const DataSpace& space,
+                                          const DataType& dtype) {
+    auto attr_id = H5Acreate2(static_cast<Derivate*>(this)->getId(),
+                              attribute_name.c_str(),
+                              dtype._hid, space._hid, H5P_DEFAULT, H5P_DEFAULT);
+    if (attr_id < 0) {
+        HDF5ErrMapper::ToException<AttributeException>(
+            std::string("Unable to create the attribute \"") + attribute_name + "\":");
+    }
+    return Attribute(attr_id);
+}
+
+template <typename Derivate>
+template <typename Type>
+inline Attribute
+AnnotateTraits<Derivate>::createAttribute(const std::string& attribute_name,
+                                          const DataSpace& space) {
+    return createAttribute(attribute_name, space, create_and_check_datatype<Type>());
+}
+
+template <typename Derivate>
+template <typename T>
+inline Attribute
+AnnotateTraits<Derivate>::createAttribute(const std::string& attribute_name,
+                                          const T& data) {
+    Attribute att = createAttribute(
+        attribute_name,
+        DataSpace::From(data),
+        create_and_check_datatype<typename details::inspector<T>::base_type>());
+    att.write(data);
+    return att;
+}
+
+template<typename Derivate>
+inline void
+AnnotateTraits<Derivate>::deleteAttribute(const std::string& attribute_name) {
+    if (H5Adelete(static_cast<const Derivate*>(this)->getId(), attribute_name.c_str()) < 0) {
+        HDF5ErrMapper::ToException<AttributeException>(
+                std::string("Unable to delete attribute \"") + attribute_name + "\":");
+    }
+}
+
+template <typename Derivate>
+inline Attribute AnnotateTraits<Derivate>::getAttribute(
+    const std::string& attribute_name) const {
+    const auto attr_id = H5Aopen(static_cast<const Derivate*>(this)->getId(),
+                                 attribute_name.c_str(), H5P_DEFAULT);
+    if (attr_id < 0) {
+        HDF5ErrMapper::ToException<AttributeException>(
+            std::string("Unable to open the attribute \"") + attribute_name + "\":");
+    }
+    return Attribute(attr_id);
+}
+
+template <typename Derivate>
+inline size_t AnnotateTraits<Derivate>::getNumberAttributes() const {
+    int res = H5Aget_num_attrs(static_cast<const Derivate*>(this)->getId());
+    if (res < 0) {
+        HDF5ErrMapper::ToException<AttributeException>(std::string(
+            "Unable to count attributes in existing group or file"));
+    }
+    return static_cast<size_t>(res);
+}
+
+template <typename Derivate>
+inline std::vector<std::string>
+AnnotateTraits<Derivate>::listAttributeNames() const {
+
+    std::vector<std::string> names;
+    details::HighFiveIterateData iterateData(names);
+
+    size_t num_objs = getNumberAttributes();
+    names.reserve(num_objs);
+
+    if (H5Aiterate2(static_cast<const Derivate*>(this)->getId(), H5_INDEX_NAME,
+                    H5_ITER_INC, NULL,
+                    &details::internal_high_five_iterate<H5A_info_t>,
+                    static_cast<void*>(&iterateData)) < 0) {
+        HDF5ErrMapper::ToException<AttributeException>(
+            std::string("Unable to list attributes in group"));
+    }
+
+    return names;
+}
+
+template <typename Derivate>
+inline bool
+AnnotateTraits<Derivate>::hasAttribute(const std::string& attr_name) const {
+    int res = H5Aexists(static_cast<const Derivate*>(this)->getId(),
+                        attr_name.c_str());
+    if (res < 0) {
+        HDF5ErrMapper::ToException<AttributeException>(
+            std::string("Unable to check for attribute in group"));
+    }
+    return res;
+}
+
+}  // namespace HighFive
+
+#endif // H5ANNOTATE_TRAITS_MISC_HPP
diff --git a/vendor/highfive/include/highfive/bits/H5Attribute_misc.hpp b/vendor/highfive/include/highfive/bits/H5Attribute_misc.hpp
new file mode 100644
index 00000000..63ae5f3c
--- /dev/null
+++ b/vendor/highfive/include/highfive/bits/H5Attribute_misc.hpp
@@ -0,0 +1,123 @@
+﻿/*
+ *  Copyright (c), 2017, Ali Can Demiralp <ali.demiralp@rwth-aachen.de>
+ *
+ *  Distributed under the Boost Software License, Version 1.0.
+ *    (See accompanying file LICENSE_1_0.txt or copy at
+ *          http://www.boost.org/LICENSE_1_0.txt)
+ *
+ */
+#ifndef H5ATTRIBUTE_MISC_HPP
+#define H5ATTRIBUTE_MISC_HPP
+
+#include <algorithm>
+#include <functional>
+#include <numeric>
+#include <sstream>
+#include <string>
+
+#ifdef H5_USE_BOOST
+#include <boost/multi_array.hpp>
+#endif
+
+#include <H5Apublic.h>
+#include <H5Ppublic.h>
+
+#include "H5Converter_misc.hpp"
+#include "H5ReadWrite_misc.hpp"
+#include "H5Utils.hpp"
+
+namespace HighFive {
+
+inline std::string Attribute::getName() const {
+    return details::get_name([&](char *buffer, hsize_t length) {
+        return H5Aget_name(_hid, length, buffer);
+    });
+}
+
+inline size_t Attribute::getStorageSize() const {
+    return static_cast<size_t>(H5Aget_storage_size(_hid));
+}
+
+inline DataType Attribute::getDataType() const {
+    DataType res;
+    res._hid = H5Aget_type(_hid);
+    return res;
+}
+
+inline DataSpace Attribute::getSpace() const {
+    DataSpace space;
+    if ((space._hid = H5Aget_space(_hid)) < 0) {
+        HDF5ErrMapper::ToException<AttributeException>(
+            "Unable to get DataSpace out of Attribute");
+    }
+    return space;
+}
+
+inline DataSpace Attribute::getMemSpace() const { return getSpace(); }
+
+template <typename T>
+inline void Attribute::read(T& array) const {
+    const DataSpace& mem_space = getMemSpace();
+    const details::BufferInfo<T> buffer_info(getDataType(),
+        [this]() -> std::string { return this->getName(); });
+
+    if (!details::checkDimensions(mem_space, buffer_info.n_dimensions)) {
+        std::ostringstream ss;
+        ss << "Impossible to read DataSet of dimensions "
+           << mem_space.getNumberDimensions() << " into arrays of dimensions "
+           << buffer_info.n_dimensions;
+        throw DataSpaceException(ss.str());
+    }
+    details::data_converter<T> converter(mem_space);
+    read(converter.transform_read(array), buffer_info.data_type);
+    // re-arrange results
+    converter.process_result(array);
+}
+
+template <typename T>
+inline void Attribute::read(T* array, const DataType& dtype) const {
+    static_assert(!std::is_const<T>::value,
+                  "read() requires a non-const structure to read data into");
+    using element_type = typename details::inspector<T>::base_type;
+    // Auto-detect mem datatype if not provided
+    const DataType& mem_datatype =
+            dtype.empty() ? create_and_check_datatype<element_type>() : dtype;
+
+    if (H5Aread(getId(), mem_datatype.getId(),
+                static_cast<void*>(array)) < 0) {
+        HDF5ErrMapper::ToException<AttributeException>(
+            "Error during HDF5 Read: ");
+    }
+}
+
+template <typename T>
+inline void Attribute::write(const T& buffer) {
+    const DataSpace& mem_space = getMemSpace();
+    const details::BufferInfo<T> buffer_info(getDataType(),
+        [this]() -> std::string { return this->getName(); });
+
+    if (!details::checkDimensions(mem_space, buffer_info.n_dimensions)) {
+        std::ostringstream ss;
+        ss << "Impossible to write buffer of dimensions " << buffer_info.n_dimensions
+           << " into dataset of dimensions " << mem_space.getNumberDimensions();
+        throw DataSpaceException(ss.str());
+    }
+    details::data_converter<T> converter(mem_space);
+    write_raw(converter.transform_write(buffer), buffer_info.data_type);
+}
+
+template <typename T>
+inline void Attribute::write_raw(const T* buffer, const DataType& dtype) {
+    using element_type = typename details::inspector<T>::base_type;
+    const auto& mem_datatype =
+        dtype.empty() ? create_and_check_datatype<element_type>() : dtype;
+
+    if (H5Awrite(getId(), mem_datatype.getId(), buffer) < 0) {
+        HDF5ErrMapper::ToException<DataSetException>(
+            "Error during HDF5 Write: ");
+    }
+}
+
+}  // namespace HighFive
+
+#endif // H5ATTRIBUTE_MISC_HPP
diff --git a/vendor/highfive/include/highfive/bits/H5ConverterEigen_misc.hpp b/vendor/highfive/include/highfive/bits/H5ConverterEigen_misc.hpp
new file mode 100644
index 00000000..b8b2136c
--- /dev/null
+++ b/vendor/highfive/include/highfive/bits/H5ConverterEigen_misc.hpp
@@ -0,0 +1,188 @@
+/*
+ *  Copyright (c), 2020, EPFL - Blue Brain Project
+ *
+ *  Distributed under the Boost Software License, Version 1.0.
+ *    (See accompanying file LICENSE_1_0.txt or copy at
+ *          http://www.boost.org/LICENSE_1_0.txt)
+ *
+ */
+#pragma once
+
+#include <Eigen/Eigen>
+
+namespace HighFive {
+
+namespace details {
+
+
+//compute size for single Eigen Matrix
+template <typename T, int M, int N>
+inline size_t compute_total_size(const Eigen::Matrix<T,M,N>& matrix) {
+    return static_cast<size_t>(matrix.rows()) * static_cast<size_t>(matrix.cols());
+}
+
+//compute size for  std::vector of Eigens
+template <typename T, int M, int N>
+inline size_t compute_total_size(const std::vector<Eigen::Matrix<T,M,N>>& vec) {
+    return vec.size() * compute_total_size(vec[0]);
+}
+
+#ifdef H5_USE_BOOST
+// compute size for  boost::multi_array of Eigens
+template <typename T, size_t Dims>
+inline size_t compute_total_size(const boost::multi_array<T, Dims>& vec) {
+    return std::accumulate(vec.origin(), vec.origin() + vec.num_elements(), size_t{0u},
+        [](size_t so_far, const T& v) {
+            return so_far + static_cast<size_t>(v.rows()) * static_cast<size_t>(v.cols());
+        });
+}
+#endif
+
+// apply conversion to eigen matrix
+template <typename T, int M, int N>
+struct data_converter<Eigen::Matrix<T, M, N>, void> {
+
+    typedef Eigen::Matrix<T, M, N> MatrixTMN;
+
+    inline data_converter(const DataSpace& space)
+        : _dims(space.getDimensions()) {
+        assert(_dims.size() == 2);
+    }
+
+    inline T* transform_read(MatrixTMN& array) {
+        if (_dims[0] != static_cast<size_t>(array.rows()) ||
+            _dims[1] != static_cast<size_t>(array.cols())) {
+            array.resize(static_cast<typename MatrixTMN::Index>(_dims[0]),
+                         static_cast<typename MatrixTMN::Index>(_dims[1]));
+        }
+        return array.data();
+    }
+
+    inline const T* transform_write(const MatrixTMN& array) {
+        return array.data();
+    }
+
+    inline void process_result(MatrixTMN&) {}
+
+    std::vector<size_t> _dims;
+};
+
+
+template <typename T, int M, int N>
+inline void vectors_to_single_buffer(const std::vector<Eigen::Matrix<T,M,N>>& vec,
+                                     const std::vector<size_t>& dims,
+                                     const size_t current_dim,
+                                     std::vector<T>& buffer) {
+
+    check_dimensions_vector(vec.size(), dims[current_dim], current_dim);
+    for (const auto& k : vec) {
+        std::copy(k.data(), k.data() + k.size(), std::back_inserter(buffer));
+    }
+}
+
+// apply conversion to std::vector of eigen matrix
+template <typename T, int M, int N>
+struct data_converter<std::vector<Eigen::Matrix<T,M,N>>, void> {
+
+    typedef Eigen::Matrix<T, M, N> MatrixTMN;
+
+    inline data_converter(const DataSpace& space)
+        : _dims(space.getDimensions()), _space(space) {
+        assert(_dims.size() == 3);
+    }
+
+    inline T * transform_read(std::vector<MatrixTMN>& /* vec */) {
+        _vec_align.resize(compute_total_size(_space.getDimensions()));
+        return _vec_align.data();
+    }
+
+    inline const T* transform_write(const std::vector<MatrixTMN>& vec) {
+        _vec_align.reserve(compute_total_size(vec));
+        vectors_to_single_buffer<T, M, N>(vec, _dims, 0, _vec_align);
+        return _vec_align.data();
+    }
+
+    inline void process_result(std::vector<MatrixTMN>& vec) {
+        T* start = _vec_align.data();
+        if (vec.size() > 0) {
+            for(auto& v : vec){
+                v = Eigen::Map<MatrixTMN>(start, v.rows(), v.cols());
+                start += v.rows()*v.cols();
+            }
+        }
+        else if (M == -1 || N == -1) {
+            std::ostringstream ss;
+            ss << "Dynamic size(-1) used without pre-defined vector data layout.\n"
+               << "Initiliaze vector elements using Zero, i.e.:\n"
+               << "\t vector<MatrixXd> vec(5, MatrixXd::Zero(20,5))";
+            throw DataSetException(ss.str());
+        }
+        else {
+            for (size_t i = 0; i < _dims[0]; ++i) {
+                vec.emplace_back(Eigen::Map<MatrixTMN>(start, M, N));
+                start += M * N;
+            }
+        }
+    }
+
+    std::vector<size_t> _dims;
+    std::vector<typename inspector<T>::base_type> _vec_align;
+    const DataSpace& _space;
+};
+
+#ifdef H5_USE_BOOST
+template <typename T, int M, int N, std::size_t Dims>
+struct data_converter<boost::multi_array<Eigen::Matrix<T, M, N>, Dims>, void> {
+    typedef typename boost::multi_array<Eigen::Matrix<T, M, N>, Dims> MultiArrayEigen;
+
+    inline data_converter(const DataSpace& space)
+        : _dims(space.getDimensions())
+        , _space(space) {
+        assert(_dims.size() == Dims + 2);
+    }
+
+    inline T* transform_read(const MultiArrayEigen& /*array*/) {
+        _vec_align.resize(compute_total_size(_space.getDimensions()));
+        return _vec_align.data();
+    }
+
+    inline const T* transform_write(const MultiArrayEigen& array) {
+        _vec_align.reserve(compute_total_size(array));
+        for (auto e = array.origin(); e < array.origin() + array.num_elements(); ++e) {
+            std::copy(e->data(), e->data() + e->size(), std::back_inserter(_vec_align));
+        }
+        return _vec_align.data();
+    }
+
+    inline void process_result(MultiArrayEigen& vec) {
+        T* start = _vec_align.data();
+        if (M != -1 && N != -1) {
+            for (auto v = vec.origin(); v < vec.origin() + vec.num_elements(); ++v) {
+                *v = Eigen::Map<Eigen::Matrix<T, M, N>>(start, v->rows(), v->cols());
+                start += v->rows() * v->cols();
+            }
+        } else {
+            if (vec.origin()->rows() > 0 && vec.origin()->cols() > 0) {
+                const auto VEC_M = vec.origin()->rows(), VEC_N = vec.origin()->cols();
+                for (auto v = vec.origin(); v < vec.origin() + vec.num_elements(); ++v) {
+                    assert(v->rows() == VEC_M && v->cols() == VEC_N);
+                    *v = Eigen::Map<Eigen::Matrix<T, M, N>>(start, VEC_M, VEC_N);
+                    start += VEC_M * VEC_N;
+                }
+            } else {
+                throw DataSetException(
+                    "Dynamic size(-1) used without pre-defined multi_array data layout.\n"
+                    "Initialize vector elements using  MatrixXd::Zero");
+            }
+        }
+    }
+
+    std::vector<size_t> _dims;
+    const DataSpace& _space;
+    std::vector<typename inspector<T>::base_type> _vec_align;
+};
+#endif  // H5_USE_BOOST
+
+}  // namespace details
+
+}  // namespace HighFive
diff --git a/vendor/highfive/include/highfive/bits/H5Converter_misc.hpp b/vendor/highfive/include/highfive/bits/H5Converter_misc.hpp
new file mode 100644
index 00000000..ba7c9827
--- /dev/null
+++ b/vendor/highfive/include/highfive/bits/H5Converter_misc.hpp
@@ -0,0 +1,438 @@
+/*
+ *  Copyright (c), 2017, Adrien Devresse <adrien.devresse@epfl.ch>
+ *
+ *  Distributed under the Boost Software License, Version 1.0.
+ *    (See accompanying file LICENSE_1_0.txt or copy at
+ *          http://www.boost.org/LICENSE_1_0.txt)
+ *
+ */
+#ifndef H5CONVERTER_MISC_HPP
+#define H5CONVERTER_MISC_HPP
+
+#include <algorithm>
+#include <cassert>
+#include <functional>
+#include <numeric>
+#include <sstream>
+#include <string>
+#include <array>
+
+#ifdef H5_USE_BOOST
+// starting Boost 1.64, serialization header must come before ublas
+#include <boost/serialization/vector.hpp>
+#include <boost/multi_array.hpp>
+#include <boost/numeric/ublas/matrix.hpp>
+#endif
+
+#include <H5Dpublic.h>
+#include <H5Ppublic.h>
+
+#include "../H5Reference.hpp"
+#include "H5Utils.hpp"
+
+namespace HighFive {
+
+namespace details {
+
+inline bool is_1D(const std::vector<size_t>& dims) {
+    return std::count_if(dims.begin(), dims.end(), [](size_t i){ return i > 1; }) < 2;
+}
+
+inline size_t compute_total_size(const std::vector<size_t>& dims) {
+    return std::accumulate(dims.begin(), dims.end(), size_t{1u},
+                           std::multiplies<size_t>());
+}
+
+inline void check_dimensions_vector(size_t size_vec, size_t size_dataset,
+                                    size_t dimension) {
+    if (size_vec != size_dataset) {
+        std::ostringstream ss;
+        ss << "Mismatch between vector size (" << size_vec
+           << ") and dataset size (" << size_dataset;
+        ss << ") on dimension " << dimension;
+        throw DataSetException(ss.str());
+    }
+}
+
+
+// Buffer converters
+// =================
+
+// copy multi dimensional vector in C++ in one C-style multi dimensional buffer
+template <typename T>
+inline void vectors_to_single_buffer(const std::vector<T>& vec_single_dim,
+                                     const std::vector<size_t>& dims,
+                                     const size_t current_dim,
+                                     std::vector<T>& buffer) {
+
+    check_dimensions_vector(vec_single_dim.size(), dims[current_dim], current_dim);
+    buffer.insert(buffer.end(), vec_single_dim.begin(), vec_single_dim.end());
+}
+
+
+template <typename T, typename U = typename inspector<T>::base_type>
+inline void
+vectors_to_single_buffer(const std::vector<T>& vec_multi_dim,
+                         const std::vector<size_t>& dims,
+                         size_t current_dim,
+                         std::vector<U>& buffer) {
+
+    check_dimensions_vector(vec_multi_dim.size(), dims[current_dim], current_dim);
+    for (const auto& it : vec_multi_dim) {
+        vectors_to_single_buffer(it, dims, current_dim + 1, buffer);
+    }
+}
+
+// copy single buffer to multi dimensional vector, following specified dimensions
+template <typename T>
+inline typename std::vector<T>::const_iterator
+single_buffer_to_vectors(typename std::vector<T>::const_iterator begin_buffer,
+                         typename std::vector<T>::const_iterator end_buffer,
+                         const std::vector<size_t>& dims,
+                         const size_t current_dim,
+                         std::vector<T>& vec_single_dim) {
+    const auto n_elems = static_cast<long>(dims[current_dim]);
+    const auto end_copy_iter = std::min(begin_buffer + n_elems, end_buffer);
+    vec_single_dim.assign(begin_buffer, end_copy_iter);
+    return end_copy_iter;
+}
+
+template <typename T, typename U = typename inspector<T>::base_type>
+inline typename std::vector<U>::const_iterator
+single_buffer_to_vectors(typename std::vector<U>::const_iterator begin_buffer,
+                         typename std::vector<U>::const_iterator end_buffer,
+                         const std::vector<size_t>& dims,
+                         const size_t current_dim,
+                         std::vector<std::vector<T>>& vec_multi_dim) {
+    const size_t n_elems = dims[current_dim];
+    vec_multi_dim.resize(n_elems);
+
+    for (auto& subvec : vec_multi_dim) {
+        begin_buffer = single_buffer_to_vectors(
+            begin_buffer, end_buffer, dims, current_dim + 1, subvec);
+    }
+    return begin_buffer;
+}
+
+
+// DATA CONVERTERS
+// ===============
+
+// apply conversion operations to basic scalar type
+template <typename Scalar, class Enable>
+struct data_converter {
+    inline data_converter(const DataSpace&) noexcept {
+
+        static_assert((std::is_arithmetic<Scalar>::value ||
+                       std::is_enum<Scalar>::value ||
+                       std::is_same<std::string, Scalar>::value),
+                      "supported datatype should be an arithmetic value, a "
+                      "std::string or a container/array");
+    }
+
+    inline Scalar* transform_read(Scalar& datamem) const noexcept {
+        return &datamem;
+    }
+
+    inline const Scalar* transform_write(const Scalar& datamem) const noexcept {
+        return &datamem;
+    }
+
+    inline void process_result(Scalar&) const noexcept {}
+};
+
+
+// apply conversion operations to the incoming data
+// if they are a cstyle array
+template <typename CArray>
+struct data_converter<CArray,
+                      typename std::enable_if<(is_c_array<CArray>::value)>::type> {
+    inline data_converter(const DataSpace&) noexcept {}
+
+    inline CArray& transform_read(CArray& datamem) const noexcept {
+        return datamem;
+    }
+
+    inline const CArray& transform_write(const CArray& datamem) const noexcept {
+        return datamem;
+    }
+
+    inline void process_result(CArray&) const noexcept {}
+};
+
+// Generic container converter
+template <typename Container, typename T = typename inspector<Container>::base_type>
+struct container_converter {
+    typedef T value_type;
+
+    inline container_converter(const DataSpace& space)
+        : _space(space) {}
+
+    // Ship (pseudo)1D implementation
+    inline value_type* transform_read(Container& vec) const {
+        auto&& dims = _space.getDimensions();
+        if (!is_1D(dims))
+            throw DataSpaceException("Dataset cant be converted to 1D");
+        vec.resize(compute_total_size(dims));
+        return vec.data();
+    }
+
+    inline const value_type* transform_write(const Container& vec) const noexcept {
+        return vec.data();
+    }
+
+    inline void process_result(Container&) const noexcept {}
+
+    const DataSpace& _space;
+};
+
+
+// apply conversion for vectors 1D
+template <typename T>
+struct data_converter<
+    std::vector<T>,
+    typename std::enable_if<(
+        std::is_same<T, typename inspector<T>::base_type>::value &&
+        !std::is_same<T, Reference>::value
+        )>::type>
+    : public container_converter<std::vector<T>> {
+
+    using container_converter<std::vector<T>>::container_converter;
+};
+
+
+// apply conversion to std::array
+template <typename T, std::size_t S>
+struct data_converter<
+    std::array<T, S>,
+    typename std::enable_if<(
+        std::is_same<T, typename inspector<T>::base_type>::value)>::type>
+    : public container_converter<std::array<T, S>> {
+
+    inline data_converter(const DataSpace& space)
+        : container_converter<std::array<T, S>>(space)
+    {
+        auto&& dims = space.getDimensions();
+        if (!is_1D(dims)) {
+            throw DataSpaceException("Only 1D std::array supported currently.");
+        }
+        if (compute_total_size(dims) != S) {
+            std::ostringstream ss;
+            ss << "Impossible to pair DataSet with " << compute_total_size(dims)
+               << " elements into an array with " << S << " elements.";
+            throw DataSpaceException(ss.str());
+        }
+    }
+
+    inline T* transform_read(std::array<T, S>& vec) const noexcept {
+        return vec.data();
+    }
+};
+
+
+#ifdef H5_USE_BOOST
+// apply conversion to boost multi array
+template <typename T, std::size_t Dims>
+struct data_converter<boost::multi_array<T, Dims>, void>
+    : public container_converter<boost::multi_array<T, Dims>> {
+    using MultiArray = boost::multi_array<T, Dims>;
+    using value_type = typename inspector<T>::base_type;
+    using container_converter<MultiArray>::container_converter;
+
+    inline value_type* transform_read(MultiArray& array) {
+        auto&& dims = this->_space.getDimensions();
+        if (std::equal(dims.begin(), dims.end(), array.shape()) == false) {
+            boost::array<typename MultiArray::index, Dims> ext;
+            std::copy(dims.begin(), dims.end(), ext.begin());
+            array.resize(ext);
+        }
+        return array.data();
+    }
+};
+
+
+// apply conversion to boost matrix ublas
+template <typename T>
+struct data_converter<boost::numeric::ublas::matrix<T>, void>
+    : public container_converter<boost::numeric::ublas::matrix<T>> {
+    using Matrix = boost::numeric::ublas::matrix<T>;
+    using value_type = typename inspector<T>::base_type;
+
+    inline data_converter(const DataSpace& space) : container_converter<Matrix>(space) {
+        assert(space.getDimensions().size() == 2);
+    }
+
+    inline value_type* transform_read(Matrix& array) {
+        boost::array<std::size_t, 2> sizes = {{array.size1(), array.size2()}};
+        auto&& _dims = this->_space.getDimensions();
+        if (std::equal(_dims.begin(), _dims.end(), sizes.begin()) == false) {
+            array.resize(_dims[0], _dims[1], false);
+            array(0, 0) = 0; // force initialization
+        }
+        return &(array(0, 0));
+    }
+
+    inline const value_type* transform_write(const Matrix& array) const noexcept {
+        return &(array(0, 0));
+    }
+};
+#endif
+
+
+// apply conversion for vectors nested vectors
+template <typename T>
+struct data_converter<std::vector<T>,
+                      typename std::enable_if<(is_container<T>::value)>::type> {
+    using value_type = typename inspector<T>::base_type;
+
+    inline data_converter(const DataSpace& space)
+        : _dims(space.getDimensions()) {}
+
+    inline value_type* transform_read(std::vector<T>&) {
+        _vec_align.resize(compute_total_size(_dims));
+        return _vec_align.data();
+    }
+
+    inline const value_type* transform_write(const std::vector<T>& vec) {
+        _vec_align.reserve(compute_total_size(_dims));
+        vectors_to_single_buffer<T>(vec, _dims, 0, _vec_align);
+        return _vec_align.data();
+    }
+
+    inline void process_result(std::vector<T>& vec) const {
+        single_buffer_to_vectors(
+            _vec_align.cbegin(), _vec_align.cend(), _dims, 0, vec);
+    }
+
+    std::vector<size_t> _dims;
+    std::vector<typename inspector<T>::base_type> _vec_align;
+};
+
+
+// apply conversion to scalar string
+template <>
+struct data_converter<std::string, void> {
+    using value_type = const char*;  // char data is const, mutable pointer
+
+    inline data_converter(const DataSpace& space) noexcept
+        : _c_vec(nullptr)
+        , _space(space) {}
+
+    // create a C vector adapted to HDF5
+    // fill last element with NULL to identify end
+    inline value_type* transform_read(std::string&) noexcept {
+        return &_c_vec;
+    }
+
+    inline const value_type* transform_write(const std::string& str) noexcept {
+        _c_vec = str.c_str();
+        return &_c_vec;
+    }
+
+    inline void process_result(std::string& str) {
+        assert(_c_vec != nullptr);
+        str = std::string(_c_vec);
+
+        if (_c_vec != nullptr) {
+            AtomicType<std::string> str_type;
+            (void)H5Dvlen_reclaim(str_type.getId(), _space.getId(), H5P_DEFAULT,
+                                  &_c_vec);
+        }
+    }
+
+    value_type _c_vec;
+    const DataSpace& _space;
+};
+
+// apply conversion for vectors of string (dereference)
+template <>
+struct data_converter<std::vector<std::string>, void> {
+    using value_type = const char*;
+
+    inline data_converter(const DataSpace& space) noexcept
+        : _space(space) {}
+
+    // create a C vector adapted to HDF5
+    // fill last element with NULL to identify end
+    inline value_type* transform_read(std::vector<std::string>&) {
+        _c_vec.resize(_space.getDimensions()[0], NULL);
+        return _c_vec.data();
+    }
+
+    inline const value_type* transform_write(const std::vector<std::string>& vec) {
+        _c_vec.resize(vec.size() + 1, NULL);
+        std::transform(vec.begin(), vec.end(), _c_vec.begin(),
+                       [](const std::string& str){ return str.c_str(); });
+        return _c_vec.data();
+    }
+
+    inline void process_result(std::vector<std::string>& vec) {
+        vec.resize(_c_vec.size());
+        for (size_t i = 0; i < vec.size(); ++i) {
+            vec[i] = std::string(_c_vec[i]);
+        }
+
+        if (_c_vec.empty() == false && _c_vec[0] != NULL) {
+            AtomicType<std::string> str_type;
+            (void)H5Dvlen_reclaim(str_type.getId(), _space.getId(), H5P_DEFAULT,
+                                  &(_c_vec[0]));
+        }
+    }
+
+    std::vector<value_type> _c_vec;
+    const DataSpace& _space;
+};
+
+
+
+// apply conversion for fixed-string. Implements container interface
+template <std::size_t N>
+struct data_converter<FixedLenStringArray<N>, void>
+    : public container_converter<FixedLenStringArray<N>, char> {
+    using container_converter<FixedLenStringArray<N>, char>::container_converter;
+};
+
+template <>
+struct data_converter<std::vector<Reference>, void> {
+    inline data_converter(const DataSpace& space)
+        : _dims(space.getDimensions()) {
+        if (!is_1D(_dims)) {
+            throw DataSpaceException("Only 1D std::array supported currently.");
+        }
+    }
+
+    inline hobj_ref_t* transform_read(std::vector<Reference>& vec) {
+        auto total_size = compute_total_size(_dims);
+        _vec_align.resize(total_size);
+        vec.resize(total_size);
+        return _vec_align.data();
+    }
+
+    inline const hobj_ref_t* transform_write(const std::vector<Reference>& vec) {
+        _vec_align.resize(compute_total_size(_dims));
+        for (size_t i = 0; i < vec.size(); ++i) {
+            vec[i].create_ref(&_vec_align[i]);
+        }
+        return _vec_align.data();
+    }
+
+    inline void process_result(std::vector<Reference>& vec) const {
+        auto* href = const_cast<hobj_ref_t*>(_vec_align.data());
+        for (auto& ref : vec) {
+            ref = Reference(*(href++));
+        }
+    }
+
+    std::vector<size_t> _dims;
+    std::vector<typename inspector<hobj_ref_t>::base_type> _vec_align;
+};
+
+}  // namespace details
+
+}  // namespace HighFive
+
+#ifdef H5_USE_EIGEN
+#include "H5ConverterEigen_misc.hpp"
+#endif
+
+#endif // H5CONVERTER_MISC_HPP
diff --git a/vendor/highfive/include/highfive/bits/H5DataSet_misc.hpp b/vendor/highfive/include/highfive/bits/H5DataSet_misc.hpp
new file mode 100644
index 00000000..cb5bd2a1
--- /dev/null
+++ b/vendor/highfive/include/highfive/bits/H5DataSet_misc.hpp
@@ -0,0 +1,78 @@
+/*
+ *  Copyright (c), 2017, Adrien Devresse <adrien.devresse@epfl.ch>
+ *
+ *  Distributed under the Boost Software License, Version 1.0.
+ *    (See accompanying file LICENSE_1_0.txt or copy at
+ *          http://www.boost.org/LICENSE_1_0.txt)
+ *
+ */
+#ifndef H5DATASET_MISC_HPP
+#define H5DATASET_MISC_HPP
+
+#include <algorithm>
+#include <functional>
+#include <numeric>
+#include <sstream>
+#include <string>
+
+#ifdef H5_USE_BOOST
+#include <boost/multi_array.hpp>
+#endif
+
+#include <H5Dpublic.h>
+#include <H5Ppublic.h>
+
+#include "H5Utils.hpp"
+
+namespace HighFive {
+
+inline uint64_t DataSet::getStorageSize() const {
+    return H5Dget_storage_size(_hid);
+}
+
+inline DataType DataSet::getDataType() const {
+    return DataType(H5Dget_type(_hid));
+}
+
+inline DataSpace DataSet::getSpace() const {
+    DataSpace space;
+    if ((space._hid = H5Dget_space(_hid)) < 0) {
+        HDF5ErrMapper::ToException<DataSetException>(
+            "Unable to get DataSpace out of DataSet");
+    }
+    return space;
+}
+
+inline DataSpace DataSet::getMemSpace() const {
+    return getSpace();
+}
+
+inline uint64_t DataSet::getOffset() const {
+    uint64_t addr = H5Dget_offset(_hid);
+    if (addr == HADDR_UNDEF) {
+        HDF5ErrMapper::ToException<DataSetException>(
+            "Cannot get offset of DataSet.");
+    }
+    return addr;
+}
+
+inline void DataSet::resize(const std::vector<size_t>& dims) {
+
+    const size_t numDimensions = getSpace().getDimensions().size();
+    if (dims.size() != numDimensions) {
+        HDF5ErrMapper::ToException<DataSetException>(
+            "Invalid dataspace dimensions, got " + std::to_string(dims.size()) +
+            " expected " + std::to_string(numDimensions));
+    }
+
+    std::vector<hsize_t> real_dims(dims.begin(), dims.end());
+
+    if (H5Dset_extent(getId(), real_dims.data()) < 0) {
+        HDF5ErrMapper::ToException<DataSetException>(
+            "Could not resize dataset.");
+    }
+}
+
+} // namespace HighFive
+
+#endif // H5DATASET_MISC_HPP
diff --git a/vendor/highfive/include/highfive/bits/H5DataType_misc.hpp b/vendor/highfive/include/highfive/bits/H5DataType_misc.hpp
new file mode 100644
index 00000000..9e012f86
--- /dev/null
+++ b/vendor/highfive/include/highfive/bits/H5DataType_misc.hpp
@@ -0,0 +1,465 @@
+/*
+ *  Copyright (c), 2017, Adrien Devresse <adrien.devresse@epfl.ch>
+ *
+ *  Distributed under the Boost Software License, Version 1.0.
+ *    (See accompanying file LICENSE_1_0.txt or copy at
+ *          http://www.boost.org/LICENSE_1_0.txt)
+ *
+ */
+#ifndef H5DATATYPE_MISC_HPP
+#define H5DATATYPE_MISC_HPP
+
+#include <string>
+#include <complex>
+#include <cstring>
+
+#include <H5Ppublic.h>
+#include <H5Tpublic.h>
+
+
+namespace HighFive {
+
+namespace {  // unnamed
+inline DataTypeClass convert_type_class(const H5T_class_t& tclass);
+inline std::string type_class_string(DataTypeClass);
+inline hid_t create_string(std::size_t length);
+}
+
+inline bool DataType::empty() const noexcept {
+    return _hid == H5I_INVALID_HID;
+}
+
+inline DataTypeClass DataType::getClass() const {
+    return convert_type_class(H5Tget_class(_hid));
+}
+
+inline size_t DataType::getSize() const {
+    return H5Tget_size(_hid);
+}
+
+inline bool DataType::operator==(const DataType& other) const {
+    return (H5Tequal(_hid, other._hid) > 0);
+}
+
+inline bool DataType::operator!=(const DataType& other) const {
+    return !(*this == other);
+}
+
+inline bool DataType::isVariableStr() const {
+    auto var_value = H5Tis_variable_str(_hid);
+    if (var_value < 0) {
+         HDF5ErrMapper::ToException<DataTypeException>(
+            "Unable to define datatype size to variable");
+    }
+    return static_cast<bool>(var_value);
+}
+
+inline bool DataType::isFixedLenStr() const {
+    return getClass() == DataTypeClass::String && !isVariableStr();
+}
+
+inline bool DataType::isReference() const {
+    return H5Tequal(_hid, H5T_STD_REF_OBJ) > 0;
+}
+
+inline std::string DataType::string() const {
+    return type_class_string(getClass()) + std::to_string(getSize() * 8);
+}
+
+// char mapping
+template <>
+inline AtomicType<char>::AtomicType() {
+    _hid = H5Tcopy(H5T_NATIVE_CHAR);
+}
+
+template <>
+inline AtomicType<signed char>::AtomicType() {
+    _hid = H5Tcopy(H5T_NATIVE_CHAR);
+}
+
+template <>
+inline AtomicType<unsigned char>::AtomicType() {
+    _hid = H5Tcopy(H5T_NATIVE_UCHAR);
+}
+
+// short mapping
+template <>
+inline AtomicType<short>::AtomicType() {
+    _hid = H5Tcopy(H5T_NATIVE_SHORT);
+}
+
+template <>
+inline AtomicType<unsigned short>::AtomicType() {
+    _hid = H5Tcopy(H5T_NATIVE_USHORT);
+}
+
+// integer mapping
+template <>
+inline AtomicType<int>::AtomicType() {
+    _hid = H5Tcopy(H5T_NATIVE_INT);
+}
+
+template <>
+inline AtomicType<unsigned>::AtomicType() {
+    _hid = H5Tcopy(H5T_NATIVE_UINT);
+}
+
+// long mapping
+template <>
+inline AtomicType<long>::AtomicType() {
+    _hid = H5Tcopy(H5T_NATIVE_LONG);
+}
+
+template <>
+inline AtomicType<unsigned long>::AtomicType() {
+    _hid = H5Tcopy(H5T_NATIVE_ULONG);
+}
+
+// long long mapping
+template <>
+inline AtomicType<long long>::AtomicType() {
+    _hid = H5Tcopy(H5T_NATIVE_LLONG);
+}
+
+template <>
+inline AtomicType<unsigned long long>::AtomicType() {
+    _hid = H5Tcopy(H5T_NATIVE_ULLONG);
+}
+
+// float and double mapping
+template <>
+inline AtomicType<float>::AtomicType() {
+    _hid = H5Tcopy(H5T_NATIVE_FLOAT);
+}
+
+template <>
+inline AtomicType<double>::AtomicType() {
+    _hid = H5Tcopy(H5T_NATIVE_DOUBLE);
+}
+
+// boolean mapping
+template <>
+inline AtomicType<bool>::AtomicType() {
+    _hid = H5Tcopy(H5T_NATIVE_HBOOL);
+}
+
+// std string
+template <>
+inline AtomicType<std::string>::AtomicType() {
+    _hid = create_string(H5T_VARIABLE);
+}
+
+// Fixed-Length strings
+// require class specialization templated for the char length
+template <size_t StrLen>
+class AtomicType<char[StrLen]> : public DataType {
+  public:
+    inline AtomicType() : DataType(create_string(StrLen)) {}
+};
+
+template <size_t StrLen>
+class AtomicType<FixedLenStringArray<StrLen>> : public DataType {
+  public:
+    inline AtomicType() : DataType(create_string(StrLen)) {}
+};
+
+template <>
+inline AtomicType<std::complex<double> >::AtomicType() {
+    static struct ComplexType : public Object {
+        ComplexType() {
+            _hid = H5Tcreate(H5T_COMPOUND, sizeof(std::complex<double>));
+            // h5py/numpy compatible datatype
+            H5Tinsert(_hid, "r", 0, H5T_NATIVE_DOUBLE);
+            H5Tinsert(_hid, "i", sizeof(double), H5T_NATIVE_DOUBLE);
+        };
+    } complexType;
+    _hid = H5Tcopy(complexType.getId());
+}
+
+// Other cases not supported. Fail early with a user message
+template <typename T>
+AtomicType<T>::AtomicType() {
+    static_assert(details::inspector<T>::recursive_ndim == 0,
+                  "Atomic types cant be arrays, except for char[] (fixed-length strings)");
+    static_assert(details::inspector<T>::recursive_ndim > 0, "Type not supported");
+}
+
+
+// class FixedLenStringArray<N>
+
+template <std::size_t N>
+inline FixedLenStringArray<N>
+::FixedLenStringArray(const char array[][N], std::size_t length) {
+    datavec.resize(length);
+    std::memcpy(datavec[0].data(), array[0].data(), N * length);
+}
+
+template <std::size_t N>
+inline FixedLenStringArray<N>
+::FixedLenStringArray(const std::string* iter_begin, const std::string* iter_end) {
+    datavec.resize(static_cast<std::size_t>(iter_end - iter_begin));
+    for (auto& dst_array : datavec) {
+        const char* src = (iter_begin++)->c_str();
+        const size_t length = std::min(N - 1 , std::strlen(src));
+        std::memcpy(dst_array.data(), src, length);
+        dst_array[length] = 0;
+    }
+}
+
+template <std::size_t N>
+inline FixedLenStringArray<N>
+::FixedLenStringArray(const std::vector<std::string> & vec)
+    : FixedLenStringArray(&vec.front(), &vec.back()) {}
+
+template <std::size_t N>
+inline FixedLenStringArray<N>
+::FixedLenStringArray(const std::initializer_list<std::string>& init_list)
+    : FixedLenStringArray(init_list.begin(), init_list.end()) {}
+
+template <std::size_t N>
+inline void FixedLenStringArray<N>::push_back(const std::string& src) {
+    datavec.emplace_back();
+    const size_t length = std::min(N - 1 , src.length());
+    std::memcpy(datavec.back().data(), src.c_str(), length);
+    datavec.back()[length] = 0;
+}
+
+template <std::size_t N>
+inline void FixedLenStringArray<N>::push_back(const std::array<char, N>& src) {
+    datavec.emplace_back();
+    std::copy(src.begin(), src.end(), datavec.back().data());
+}
+
+template <std::size_t N>
+inline std::string FixedLenStringArray<N>::getString(std::size_t i) const {
+    return std::string(datavec[i].data());
+}
+
+// Internal
+// Reference mapping
+template <>
+inline AtomicType<Reference>::AtomicType() {
+    _hid = H5Tcopy(H5T_STD_REF_OBJ);
+}
+
+inline size_t find_first_atomic_member_size(hid_t hid)
+{
+    // Recursive exit condition
+    if (H5Tget_class(hid) != H5T_COMPOUND) {
+        return H5Tget_size(hid);
+    }
+
+    auto number_of_members = H5Tget_nmembers(hid);
+    if (number_of_members == -1) {
+        throw DataTypeException("Cannot get members of CompoundType with hid: " +
+                                std::to_string(hid));
+    }
+    if (number_of_members == 0) {
+        throw DataTypeException("No members defined for CompoundType with hid: " +
+                                std::to_string(hid));
+    }
+
+    auto member_type = H5Tget_member_type(hid, 0);
+    auto size = find_first_atomic_member_size(member_type);
+    H5Tclose(member_type);
+    return size;
+}
+
+// Calculate the padding required to align an element of a struct
+#define _H5_STRUCT_PADDING(current_size, member_size) (((member_size) - (current_size)) % (member_size))
+
+inline void CompoundType::create(size_t size) {
+    if (size == 0) {
+        size_t current_size = 0, max_atomic_size = 0;
+
+        // Do a first pass to find the total size of the compound datatype
+        for (auto& member: members) {
+            size_t member_size = H5Tget_size(member.base_type.getId());
+
+            if (member_size == 0) {
+                throw DataTypeException("Cannot get size of DataType with hid: " +
+                                        std::to_string(member.base_type.getId()));
+            }
+
+            size_t first_atomic_size = find_first_atomic_member_size(member.base_type.getId());
+
+            // Set the offset of this member within the struct according to the
+            // standard alignment rules. The c++ standard specifies that:
+            // > objects have an alignment requirement of which their size is a multiple
+            member.offset = current_size + _H5_STRUCT_PADDING(current_size, first_atomic_size);
+
+            // Set the current size to the end of the new member
+            current_size = member.offset + member_size;
+
+            // Keep track of the highest atomic member size because it's needed
+            // for the padding of the complete compound type.
+            max_atomic_size = std::max(max_atomic_size, first_atomic_size);
+        }
+
+        size = current_size + _H5_STRUCT_PADDING(current_size, max_atomic_size);
+    }
+
+    // Create the HDF5 type
+    if((_hid = H5Tcreate(H5T_COMPOUND, size)) < 0) {
+        HDF5ErrMapper::ToException<DataTypeException>(
+            "Could not create new compound datatype");
+    }
+
+    // Loop over all the members and insert them into the datatype
+    for (const auto& member: members) {
+        if(H5Tinsert(_hid, member.name.c_str(), member.offset, member.base_type.getId()) < 0) {
+            HDF5ErrMapper::ToException<DataTypeException>(
+                "Could not add new member to datatype"
+            );
+        }
+    }
+}
+
+#undef _H5_STRUCT_PADDING
+
+inline void CompoundType::commit(const Object& object, const std::string& name) const {
+    H5Tcommit2(object.getId(), name.c_str(), getId(), H5P_DEFAULT, H5P_DEFAULT, H5P_DEFAULT);
+}
+
+template<typename T>
+inline void EnumType<T>::create() {
+    // Create the HDF5 type
+    if((_hid = H5Tenum_create(AtomicType<typename std::underlying_type<T>::type>{}.getId())) < 0) {
+        HDF5ErrMapper::ToException<DataTypeException>(
+            "Could not create new enum datatype");
+    }
+
+    // Loop over all the members and insert them into the datatype
+    for (const auto& member: members) {
+        if(H5Tenum_insert(_hid, member.name.c_str(), &(member.value)) < 0) {
+            HDF5ErrMapper::ToException<DataTypeException>(
+                "Could not add new member to this enum datatype"
+            );
+        }
+    }
+}
+
+template<typename T>
+inline void EnumType<T>::commit(const Object& object, const std::string& name) const {
+    H5Tcommit2(object.getId(), name.c_str(), getId(), H5P_DEFAULT, H5P_DEFAULT, H5P_DEFAULT);
+}
+
+namespace {
+
+inline hid_t create_string(size_t length){
+    hid_t _hid = H5Tcopy(H5T_C_S1);
+    if (H5Tset_size(_hid, length) < 0) {
+        HDF5ErrMapper::ToException<DataTypeException>(
+            "Unable to define datatype size to variable");
+    }
+    // define encoding to UTF-8 by default
+    H5Tset_cset(_hid, H5T_CSET_UTF8);
+    return _hid;
+}
+
+
+inline DataTypeClass convert_type_class(const H5T_class_t& tclass) {
+    switch(tclass) {
+        case H5T_TIME:
+            return DataTypeClass::Time;
+        case H5T_INTEGER:
+            return DataTypeClass::Integer;
+        case H5T_FLOAT:
+            return DataTypeClass::Float;
+        case H5T_STRING:
+            return DataTypeClass::String;
+        case H5T_BITFIELD:
+            return DataTypeClass::BitField;
+        case H5T_OPAQUE:
+            return DataTypeClass::Opaque;
+        case H5T_COMPOUND:
+            return DataTypeClass::Compound;
+        case H5T_REFERENCE:
+            return DataTypeClass::Reference;
+        case H5T_ENUM:
+            return DataTypeClass::Enum;
+        case H5T_VLEN:
+            return DataTypeClass::VarLen;
+        case H5T_ARRAY:
+            return DataTypeClass::Array;
+        case H5T_NO_CLASS:
+        case H5T_NCLASSES:
+        default:
+            return DataTypeClass::Invalid;
+    }
+}
+
+
+inline std::string type_class_string(DataTypeClass tclass) {
+    switch(tclass) {
+        case DataTypeClass::Time:
+            return "Time";
+        case DataTypeClass::Integer:
+            return "Integer";
+        case DataTypeClass::Float:
+            return "Float";
+        case DataTypeClass::String:
+            return "String";
+        case DataTypeClass::BitField:
+            return "BitField";
+        case DataTypeClass::Opaque:
+            return "Opaque";
+        case DataTypeClass::Compound:
+            return "Compound";
+        case DataTypeClass::Reference:
+            return "Reference";
+        case DataTypeClass::Enum:
+            return "Enum";
+        case DataTypeClass::VarLen:
+            return "Varlen";
+        case DataTypeClass::Array:
+            return "Array";
+        default:
+            return "(Invalid)";
+    }
+}
+
+}  // unnamed namespace
+
+
+/// \brief Create a DataType instance representing type T
+template <typename T>
+inline DataType create_datatype() {
+    return AtomicType<T>();
+}
+
+
+/// \brief Create a DataType instance representing type T and perform a sanity check on its size
+template <typename T>
+inline DataType create_and_check_datatype() {
+
+    DataType t = create_datatype<T>();
+    if (t.empty()) {
+        throw DataTypeException("Type given to create_and_check_datatype is not valid");
+    }
+
+    // Skip check if the base type is a variable length string
+    if (t.isVariableStr()) {
+        return t;
+    }
+
+    // Check that the size of the template type matches the size that HDF5 is
+    // expecting.
+    if (t.isReference() || t.isFixedLenStr()) {
+        return t;
+    }
+    if (sizeof(T) != t.getSize()) {
+        std::ostringstream ss;
+        ss << "Size of array type " << sizeof(T)
+           << " != that of memory datatype " << t.getSize()
+           << std::endl;
+        throw DataTypeException(ss.str());
+    }
+
+    return t;
+}
+
+}  // namespace HighFive
+
+
+#endif // H5DATATYPE_MISC_HPP
diff --git a/vendor/highfive/include/highfive/bits/H5Dataspace_misc.hpp b/vendor/highfive/include/highfive/bits/H5Dataspace_misc.hpp
new file mode 100644
index 00000000..dc53032c
--- /dev/null
+++ b/vendor/highfive/include/highfive/bits/H5Dataspace_misc.hpp
@@ -0,0 +1,172 @@
+/*
+ *  Copyright (c), 2017, Adrien Devresse <adrien.devresse@epfl.ch>
+ *
+ *  Distributed under the Boost Software License, Version 1.0.
+ *    (See accompanying file LICENSE_1_0.txt or copy at
+ *          http://www.boost.org/LICENSE_1_0.txt)
+ *
+ */
+#ifndef H5DATASPACE_MISC_HPP
+#define H5DATASPACE_MISC_HPP
+
+#include <array>
+#include <initializer_list>
+#include <vector>
+#include <numeric>
+
+#include <H5Spublic.h>
+
+#include "H5Utils.hpp"
+
+namespace HighFive {
+
+inline DataSpace::DataSpace(const std::vector<size_t>& dims)
+    : DataSpace(dims.begin(), dims.end()) {}
+
+template <size_t N>
+inline DataSpace::DataSpace(const std::array<size_t, N>& dims)
+    : DataSpace(dims.begin(), dims.end()) {}
+
+inline DataSpace::DataSpace(const std::initializer_list<size_t>& items)
+    : DataSpace(std::vector<size_t>(items)) {}
+
+template<typename... Args>
+    inline DataSpace::DataSpace(size_t dim1, Args... dims)
+    : DataSpace(std::vector<size_t>{dim1, static_cast<size_t>(dims)...}) {}
+
+template <class IT, typename>
+inline DataSpace::DataSpace(const IT begin, const IT end) {
+    std::vector<hsize_t> real_dims(begin, end);
+
+    if ((_hid = H5Screate_simple(int(real_dims.size()), real_dims.data(),
+                                 NULL)) < 0) {
+        throw DataSpaceException("Impossible to create dataspace");
+    }
+}
+
+inline DataSpace::DataSpace(const std::vector<size_t>& dims,
+                            const std::vector<size_t>& maxdims) {
+
+    if (dims.size() != maxdims.size()) {
+        throw DataSpaceException("dims and maxdims must be the same length.");
+    }
+
+    std::vector<hsize_t> real_dims(dims.begin(), dims.end());
+    std::vector<hsize_t> real_maxdims(maxdims.begin(), maxdims.end());
+
+    // Replace unlimited flag with actual HDF one
+    std::replace(real_maxdims.begin(), real_maxdims.end(),
+                 static_cast<hsize_t>(DataSpace::UNLIMITED), H5S_UNLIMITED);
+
+    if ((_hid = H5Screate_simple(int(dims.size()), real_dims.data(),
+                                 real_maxdims.data())) < 0) {
+        throw DataSpaceException("Impossible to create dataspace");
+    }
+} // namespace HighFive
+
+inline DataSpace::DataSpace(DataSpace::DataspaceType dtype) {
+    H5S_class_t h5_dataspace_type;
+    switch (dtype) {
+    case DataSpace::datascape_scalar:
+        h5_dataspace_type = H5S_SCALAR;
+        break;
+    case DataSpace::datascape_null:
+        h5_dataspace_type = H5S_NULL;
+        break;
+    default:
+        throw DataSpaceException("Invalid dataspace type: should be "
+                                 "datascape_scalar or datascape_null");
+    }
+
+    if ((_hid = H5Screate(h5_dataspace_type)) < 0) {
+        throw DataSpaceException("Unable to create dataspace");
+    }
+}
+
+inline DataSpace DataSpace::clone() const {
+    DataSpace res;
+    if ((res._hid = H5Scopy(_hid)) < 0) {
+        throw DataSpaceException("Unable to copy dataspace");
+    }
+    return res;
+}
+
+inline size_t DataSpace::getNumberDimensions() const {
+    const int ndim = H5Sget_simple_extent_ndims(_hid);
+    if (ndim < 0) {
+        HDF5ErrMapper::ToException<DataSetException>(
+            "Unable to get dataspace number of dimensions");
+    }
+    return size_t(ndim);
+}
+
+inline std::vector<size_t> DataSpace::getDimensions() const {
+    std::vector<hsize_t> dims(getNumberDimensions());
+    if (!dims.empty()) {
+        if (H5Sget_simple_extent_dims(_hid, dims.data(), NULL) < 0) {
+            HDF5ErrMapper::ToException<DataSetException>(
+                "Unable to get dataspace dimensions");
+        }
+    }
+    return details::to_vector_size_t(std::move(dims));
+}
+
+inline size_t DataSpace::getElementCount() const {
+    const std::vector<size_t>& dims = getDimensions();
+    return std::accumulate(dims.begin(), dims.end(), size_t{1u},
+                           std::multiplies<size_t>());
+}
+
+inline std::vector<size_t> DataSpace::getMaxDimensions() const {
+    std::vector<hsize_t> maxdims(getNumberDimensions());
+    if (H5Sget_simple_extent_dims(_hid, NULL, maxdims.data()) < 0) {
+        HDF5ErrMapper::ToException<DataSetException>(
+            "Unable to get dataspace dimensions");
+    }
+
+    std::replace(maxdims.begin(), maxdims.end(), H5S_UNLIMITED,
+                 static_cast<hsize_t>(DataSpace::UNLIMITED));
+    return details::to_vector_size_t(maxdims);
+}
+
+template <typename T>
+inline DataSpace DataSpace::From(const T& value) {
+    auto dims = details::inspector<T>::getDimensions(value);
+    return DataSpace(dims);
+}
+
+template <std::size_t N, std::size_t Width>
+inline DataSpace DataSpace::FromCharArrayStrings(const char(&)[N][Width]) {
+    return DataSpace(N);
+}
+
+namespace details {
+
+/// dimension checks @internal
+inline bool checkDimensions(const DataSpace& mem_space, size_t input_dims) {
+    size_t dataset_dims = mem_space.getNumberDimensions();
+    if (input_dims == dataset_dims)
+        return true;
+
+    const std::vector<size_t>& dims = mem_space.getDimensions();
+    for (auto i = dims.crbegin(); i != --dims.crend() && *i == 1; ++i)
+        --dataset_dims;
+
+    if (input_dims == dataset_dims)
+        return true;
+
+    dataset_dims = dims.size();
+    for (auto i = dims.cbegin(); i != --dims.cend() && *i == 1; ++i)
+        --dataset_dims;
+
+    if (input_dims == dataset_dims)
+        return true;
+
+    // The final tests is for scalars
+    return input_dims == 0 && dataset_dims == 1 && dims[dims.size() - 1] == 1;
+}
+
+} // namespace details
+} // namespace HighFive
+
+#endif // H5DATASPACE_MISC_HPP
diff --git a/vendor/highfive/include/highfive/bits/H5Exception_misc.hpp b/vendor/highfive/include/highfive/bits/H5Exception_misc.hpp
new file mode 100644
index 00000000..3a897ef3
--- /dev/null
+++ b/vendor/highfive/include/highfive/bits/H5Exception_misc.hpp
@@ -0,0 +1,67 @@
+/*
+ *  Copyright (c), 2017, Adrien Devresse <adrien.devresse@epfl.ch>
+ *
+ *  Distributed under the Boost Software License, Version 1.0.
+ *    (See accompanying file LICENSE_1_0.txt or copy at
+ *          http://www.boost.org/LICENSE_1_0.txt)
+ *
+ */
+#ifndef H5EXCEPTION_MISC_HPP
+#define H5EXCEPTION_MISC_HPP
+
+#include <cstdlib>
+#include <sstream>
+
+#include <H5Epublic.h>
+
+namespace HighFive {
+
+struct HDF5ErrMapper {
+
+    template <typename ExceptionType>
+    static inline herr_t stackWalk(unsigned n, const H5E_error2_t* err_desc,
+                                   void* client_data) {
+        auto** e_iter = static_cast<ExceptionType**>(client_data);
+        (void)n;
+
+        const char* major_err = H5Eget_major(err_desc->maj_num);
+        const char* minor_err = H5Eget_minor(err_desc->min_num);
+
+        std::ostringstream oss;
+        oss << '(' << major_err << ") " << minor_err;
+
+        auto* e = new ExceptionType(oss.str());
+        e->_err_major = err_desc->maj_num;
+        e->_err_minor = err_desc->min_num;
+        (*e_iter)->_next.reset(e);
+        *e_iter = e;
+        return 0;
+    }
+
+    template <typename ExceptionType>
+    [[noreturn]] static inline void ToException(const std::string& prefix_msg) {
+
+        hid_t err_stack = H5Eget_current_stack();
+        if (err_stack >= 0) {
+            ExceptionType e("");
+            ExceptionType* e_iter = &e;
+
+            H5Ewalk2(err_stack, H5E_WALK_UPWARD,
+                     &HDF5ErrMapper::stackWalk<ExceptionType>, &e_iter);
+            H5Eclear2(err_stack);
+
+            const char* next_err_msg = (e.nextException() != NULL)
+                                           ? (e.nextException()->what())
+                                           : ("");
+
+            e.setErrorMsg(prefix_msg + " " + next_err_msg);
+            throw e;
+        }
+        // throw generic error, unrecognized error
+        throw ExceptionType(prefix_msg + ": Unknown HDF5 error");
+    }
+};
+
+}  // namespace HighFive
+
+#endif // H5OBJECT_MISC_HPP
diff --git a/vendor/highfive/include/highfive/bits/H5FileDriver_misc.hpp b/vendor/highfive/include/highfive/bits/H5FileDriver_misc.hpp
new file mode 100644
index 00000000..90f01305
--- /dev/null
+++ b/vendor/highfive/include/highfive/bits/H5FileDriver_misc.hpp
@@ -0,0 +1,52 @@
+/*
+ *  Copyright (c), 2017-2018, Adrien Devresse <adrien.devresse@epfl.ch>
+ *                            Juan Hernando <juan.hernando@epfl.ch>
+ *
+ *  Distributed under the Boost Software License, Version 1.0.
+ *    (See accompanying file LICENSE_1_0.txt or copy at
+ *          http://www.boost.org/LICENSE_1_0.txt)
+ *
+ */
+#ifndef H5FILEDRIVER_MISC_HPP
+#define H5FILEDRIVER_MISC_HPP
+
+#include <H5Ppublic.h>
+
+#ifdef H5_HAVE_PARALLEL
+#include <H5FDmpi.h>
+#endif
+
+namespace HighFive {
+
+namespace {
+
+template <typename Comm, typename Info>
+class MPIOFileAccess
+{
+public:
+  MPIOFileAccess(Comm comm, Info info)
+      : _comm(comm)
+      , _info(info)
+  {}
+
+  void apply(const hid_t list) const {
+    if (H5Pset_fapl_mpio(list, _comm, _info) < 0) {
+        HDF5ErrMapper::ToException<FileException>(
+            "Unable to set-up MPIO Driver configuration");
+    }
+  }
+private:
+  Comm _comm;
+  Info _info;
+};
+
+}  //namespace
+
+template <typename Comm, typename Info>
+inline MPIOFileDriver::MPIOFileDriver(Comm comm, Info info) {
+    add(MPIOFileAccess<Comm, Info>(comm, info));
+}
+
+} // namespace HighFive
+
+#endif // H5FILEDRIVER_MISC_HPP
diff --git a/vendor/highfive/include/highfive/bits/H5File_misc.hpp b/vendor/highfive/include/highfive/bits/H5File_misc.hpp
new file mode 100644
index 00000000..304205ab
--- /dev/null
+++ b/vendor/highfive/include/highfive/bits/H5File_misc.hpp
@@ -0,0 +1,96 @@
+/*
+ *  Copyright (c), 2017, Adrien Devresse <adrien.devresse@epfl.ch>
+ *
+ *  Distributed under the Boost Software License, Version 1.0.
+ *    (See accompanying file LICENSE_1_0.txt or copy at
+ *          http://www.boost.org/LICENSE_1_0.txt)
+ *
+ */
+#ifndef H5FILE_MISC_HPP
+#define H5FILE_MISC_HPP
+
+#include <string>
+
+#include <H5Fpublic.h>
+
+#include "../H5Utility.hpp"
+#include "H5Utils.hpp"
+
+namespace HighFive {
+
+namespace {  // unnamed
+
+// libhdf5 uses a preprocessor trick on their oflags
+// we can not declare them constant without a mapper
+inline unsigned convert_open_flag(unsigned openFlags) {
+    unsigned res_open = 0;
+    if (openFlags & File::ReadOnly)
+        res_open |= H5F_ACC_RDONLY;
+    if (openFlags & File::ReadWrite)
+        res_open |= H5F_ACC_RDWR;
+    if (openFlags & File::Create)
+        res_open |= H5F_ACC_CREAT;
+    if (openFlags & File::Truncate)
+        res_open |= H5F_ACC_TRUNC;
+    if (openFlags & File::Excl)
+        res_open |= H5F_ACC_EXCL;
+    return res_open;
+}
+}  // namespace
+
+
+inline File::File(const std::string& filename, unsigned openFlags,
+                  const FileAccessProps& fileAccessProps) {
+    openFlags = convert_open_flag(openFlags);
+
+    unsigned createMode = openFlags & (H5F_ACC_TRUNC | H5F_ACC_EXCL);
+    unsigned openMode = openFlags & (H5F_ACC_RDWR | H5F_ACC_RDONLY);
+    bool mustCreate = createMode > 0;
+    bool openOrCreate = (openFlags & H5F_ACC_CREAT) > 0;
+
+    // open is default. It's skipped only if flags require creation
+    // If open fails it will try create() if H5F_ACC_CREAT is set
+    if (!mustCreate) {
+        // Silence open errors if create is allowed
+        std::unique_ptr<SilenceHDF5> silencer;
+        if (openOrCreate) silencer.reset(new SilenceHDF5());
+
+        _hid = H5Fopen(filename.c_str(), openMode, fileAccessProps.getId());
+
+        if (isValid()) return;  // Done
+
+        if (openOrCreate) {
+            // Will attempt to create ensuring wont clobber any file
+            createMode = H5F_ACC_EXCL;
+        } else {
+            HDF5ErrMapper::ToException<FileException>(
+                std::string("Unable to open file " + filename));
+        }
+    }
+
+    if ((_hid = H5Fcreate(filename.c_str(), createMode, H5P_DEFAULT,
+                          fileAccessProps.getId())) < 0) {
+        HDF5ErrMapper::ToException<FileException>(
+            std::string("Unable to create file " + filename));
+    }
+}
+
+inline const std::string& File::getName() const noexcept {
+    if (_filename.empty()) {
+        _filename = details::get_name([this](char* buffer, hsize_t length) {
+            return H5Fget_name(getId(), buffer, length);
+        });
+    }
+    return _filename;
+}
+
+inline void File::flush() {
+    if (H5Fflush(_hid, H5F_SCOPE_GLOBAL) < 0) {
+        HDF5ErrMapper::ToException<FileException>(
+            std::string("Unable to flush file " + getName()));
+    }
+}
+
+}  // namespace HighFive
+
+#endif  // H5FILE_MISC_HPP
diff --git a/vendor/highfive/include/highfive/bits/H5Iterables_misc.hpp b/vendor/highfive/include/highfive/bits/H5Iterables_misc.hpp
new file mode 100644
index 00000000..90b5324e
--- /dev/null
+++ b/vendor/highfive/include/highfive/bits/H5Iterables_misc.hpp
@@ -0,0 +1,55 @@
+/*
+ *  Copyright (c), 2017, Adrien Devresse <adrien.devresse@epfl.ch>
+ *
+ *  Distributed under the Boost Software License, Version 1.0.
+ *    (See accompanying file LICENSE_1_0.txt or copy at
+ *          http://www.boost.org/LICENSE_1_0.txt)
+ *
+ */
+#ifndef H5ITERABLE_MISC_HPP
+#define H5ITERABLE_MISC_HPP
+
+#include <exception>
+#include <string>
+#include <vector>
+
+#include <H5Ipublic.h>
+
+namespace HighFive {
+
+namespace details {
+
+// iterator for H5 iterate
+
+struct HighFiveIterateData {
+    inline HighFiveIterateData(std::vector<std::string>& my_names)
+        : names(my_names), err(NULL) {}
+
+    std::vector<std::string>& names;
+    std::exception* err;
+
+    inline void throwIfError() {
+        if (err) {
+            throw * err;
+        }
+    }
+};
+
+template <typename InfoType>
+inline herr_t internal_high_five_iterate(
+		hid_t /*id*/, const char* name, const InfoType* /*info*/, void* op_data) {
+    auto* data = static_cast<HighFiveIterateData*>(op_data);
+    try {
+        data->names.emplace_back(name);
+        return 0;
+    } catch (...) {
+        data->err =
+            new ObjectException("Exception during H5Iterate, abort listing");
+    }
+    return -1;
+}
+
+} // end details
+}
+
+#endif // H5ITERABLE_MISC_HPP
diff --git a/vendor/highfive/include/highfive/bits/H5Node_traits.hpp b/vendor/highfive/include/highfive/bits/H5Node_traits.hpp
new file mode 100644
index 00000000..f9b76adc
--- /dev/null
+++ b/vendor/highfive/include/highfive/bits/H5Node_traits.hpp
@@ -0,0 +1,213 @@
+/*
+ *  Copyright (c), 2017, Adrien Devresse <adrien.devresse@epfl.ch>
+ *
+ *  Distributed under the Boost Software License, Version 1.0.
+ *    (See accompanying file LICENSE_1_0.txt or copy at
+ *          http://www.boost.org/LICENSE_1_0.txt)
+ *
+ */
+#ifndef H5NODE_TRAITS_HPP
+#define H5NODE_TRAITS_HPP
+
+#include <string>
+
+#include "../H5PropertyList.hpp"
+#include "H5_definitions.hpp"
+
+namespace HighFive {
+
+///
+/// \brief NodeTraits: Base class for Group and File
+///
+template <typename Derivate>
+class NodeTraits {
+  public:
+    ///
+    /// \brief createDataSet Create a new dataset in the current file of
+    /// datatype type and of size space
+    /// \param dataset_name identifier of the dataset
+    /// \param space Associated DataSpace, see \ref DataSpace for more information
+    /// \param type Type of Data
+    /// \param createProps A property list with data set creation properties
+    /// \param accessProps A property list with data set access properties
+    /// \param parents Create intermediate groups if needed. Default: true.
+    /// \return DataSet Object
+    DataSet
+    createDataSet(const std::string& dataset_name,
+                  const DataSpace& space,
+                  const DataType& type,
+                  const DataSetCreateProps& createProps = DataSetCreateProps::Default(),
+                  const DataSetAccessProps& accessProps = DataSetAccessProps::Default(),
+                  bool parents = true);
+
+    ///
+    /// \brief createDataSet create a new dataset in the current file with a
+    /// size specified by space
+    /// \param dataset_name identifier of the dataset
+    /// \param space Associated DataSpace, see \ref DataSpace for more information
+    /// \param createProps A property list with data set creation properties
+    /// \param accessProps A property list with data set access properties
+    /// \param parents Create intermediate groups if needed. Default: true.
+    /// \return DataSet Object
+    template <typename Type>
+    DataSet
+    createDataSet(const std::string& dataset_name,
+                  const DataSpace& space,
+                  const DataSetCreateProps& createProps = DataSetCreateProps::Default(),
+                  const DataSetAccessProps& accessProps = DataSetAccessProps::Default(),
+                  bool parents = true);
+
+    ///
+    /// \brief createDataSet create a new dataset in the current file and
+    /// write to it, inferring the DataSpace from the data.
+    /// \param dataset_name identifier of the dataset
+    /// \param data Associated data, must support DataSpace::From, see
+    /// \ref DataSpace for more information
+    /// \param createProps A property list with data set creation properties
+    /// \param accessProps A property list with data set access properties
+    /// \param parents Create intermediate groups if needed. Default: true.
+    /// \return DataSet Object
+    template <typename T>
+    DataSet
+    createDataSet(const std::string& dataset_name,
+                  const T& data,
+                  const DataSetCreateProps& createProps = DataSetCreateProps::Default(),
+                  const DataSetAccessProps& accessProps = DataSetAccessProps::Default(),
+                  bool parents = true);
+
+
+    template <std::size_t N>
+    DataSet
+    createDataSet(const std::string& dataset_name,
+                  const FixedLenStringArray<N>& data,
+                  const DataSetCreateProps& createProps = DataSetCreateProps::Default(),
+                  const DataSetAccessProps& accessProps = DataSetAccessProps::Default(),
+                  bool parents = true);
+
+    ///
+    /// \brief get an existing dataset in the current file
+    /// \param dataset_name
+    /// \param accessProps property list to configure dataset chunk cache
+    /// \return return the named dataset, or throw exception if not found
+    DataSet getDataSet(
+        const std::string& dataset_name,
+        const DataSetAccessProps& accessProps = DataSetAccessProps::Default()) const;
+
+    ///
+    /// \brief create a new group, and eventually intermediate groups
+    /// \param group_name
+    /// \param parents Create intermediate groups if needed. Default: true.
+    /// \return the group object
+    Group createGroup(const std::string& group_name, bool parents = true);
+
+    ///
+    /// \brief open an existing group with the name group_name
+    /// \param group_name
+    /// \return the group object
+    Group getGroup(const std::string& group_name) const;
+
+    ///
+    /// \brief return the number of leaf objects of the node / group
+    /// \return number of leaf objects
+    size_t getNumberObjects() const;
+
+    ///
+    /// \brief return the name of the object with the given index
+    /// \return the name of the object
+    std::string getObjectName(size_t index) const;
+
+    ///
+    /// \brief moves an object and its content within an HDF5 file.
+    /// \param src_path relative path of the object to current File/Group
+    /// \param dest_path new relative path of the object to current File/Group
+    /// \param parents Create intermediate groups if needed. Default: true.
+    /// \return boolean that is true if the move was successful
+    bool rename(const std::string& src_path,
+                const std::string& dest_path,
+                bool parents = true) const;
+
+    ///
+    /// \brief list all leaf objects name of the node / group
+    /// \return number of leaf objects
+    std::vector<std::string> listObjectNames() const;
+
+    ///
+    /// \brief check a dataset or group exists in the current node / group
+    /// \param node_name dataset/group name to check
+    /// \return true if a dataset/group with the associated name exists, or false
+    bool exist(const std::string& node_name) const;
+
+    ///
+    /// \brief unlink the given dataset or group
+    /// \param node_name dataset/group name to unlink
+    void unlink(const std::string& node_name) const;
+
+    ///
+    /// \brief Returns the kind of link of the given name (soft, hard...)
+    /// \param node_name The entry to check, path relative to the current group
+    LinkType getLinkType(const std::string& node_name) const;
+
+    ///
+    /// \brief A shorthand to get the kind of object pointed to (group, dataset, type...)
+    /// \param node_name The entry to check, path relative to the current group
+    ObjectType getObjectType(const std::string& node_name) const;
+
+    ///
+    /// \brief A shorthand to create softlink to any object which provides `getPath`
+    /// The link will be created with default properties along with required parent groups
+    template <typename T, typename = decltype(&T::getPath)>
+    void createSoftLink(const std::string& linkName, const T& obj) {
+        static_assert(!std::is_same<T, Attribute>::value,
+                      "hdf5 doesn't support soft links to Attributes");
+        createSoftLink(linkName, obj.getPath());
+    }
+
+    ///
+    /// \brief Creates softlinks
+    /// \param link_name The name of the link
+    /// \param obj_path The target object path
+    /// \param linkCreateProps A Link_Create property list. Notice "parents=true" overrides
+    /// \param linkAccessProps The Link_Access property list
+    /// \param parents Whether parent groups should be created: Default: true
+    void createSoftLink(const std::string& link_name,
+                        const std::string& obj_path,
+                        LinkCreateProps linkCreateProps = LinkCreateProps(),
+                        const LinkAccessProps& linkAccessProps = LinkAccessProps(),
+                        const bool parents = true);
+
+    void createExternalLink(const std::string& link_name,
+                            const std::string& h5_file,
+                            const std::string& obj_path,
+                            LinkCreateProps linkCreateProps = LinkCreateProps(),
+                            const LinkAccessProps& linkAccessProps = LinkAccessProps(),
+                            const bool parents = true);
+
+  private:
+    typedef Derivate derivate_type;
+
+    // A wrapper over the low-level H5Lexist
+    // It makes behavior consistent among versions and by default transforms
+    // errors to exceptions
+    bool _exist(const std::string& node_name, bool raise_errors = true) const;
+
+    // Opens an arbitrary object to obtain info
+    Object _open(const std::string& node_name,
+                 const DataSetAccessProps& accessProps = DataSetAccessProps::Default()) const;
+};
+
+
+///
+/// \brief The possible types of group entries (link concept)
+///
+enum class LinkType {
+    Hard,
+    Soft,
+    External,
+    Other  // Reserved or User-defined
+};
+
+
+}  // namespace HighFive
+
+
+#endif  // H5NODE_TRAITS_HPP
diff --git a/vendor/highfive/include/highfive/bits/H5Node_traits_misc.hpp b/vendor/highfive/include/highfive/bits/H5Node_traits_misc.hpp
new file mode 100644
index 00000000..a34067cb
--- /dev/null
+++ b/vendor/highfive/include/highfive/bits/H5Node_traits_misc.hpp
@@ -0,0 +1,332 @@
+ /*
+ *  Copyright (c), 2017, Adrien Devresse <adrien.devresse@epfl.ch>
+ *
+ *  Distributed under the Boost Software License, Version 1.0.
+ *    (See accompanying file LICENSE_1_0.txt or copy at
+ *          http://www.boost.org/LICENSE_1_0.txt)
+ *
+ */
+#ifndef H5NODE_TRAITS_MISC_HPP
+#define H5NODE_TRAITS_MISC_HPP
+
+#include <string>
+#include <vector>
+
+#include <H5Apublic.h>
+#include <H5Dpublic.h>
+#include <H5Fpublic.h>
+#include <H5Gpublic.h>
+#include <H5Ppublic.h>
+#include <H5Tpublic.h>
+
+#include "../H5DataSet.hpp"
+#include "../H5Group.hpp"
+#include "../H5Selection.hpp"
+#include "../H5Utility.hpp"
+#include "H5DataSet_misc.hpp"
+#include "H5Iterables_misc.hpp"
+#include "H5Selection_misc.hpp"
+#include "H5Slice_traits_misc.hpp"
+
+namespace HighFive {
+
+
+template <typename Derivate>
+inline DataSet
+NodeTraits<Derivate>::createDataSet(const std::string& dataset_name,
+                                    const DataSpace& space,
+                                    const DataType& dtype,
+                                    const DataSetCreateProps& createProps,
+                                    const DataSetAccessProps& accessProps,
+                                    bool parents) {
+    LinkCreateProps lcpl;
+    lcpl.add(CreateIntermediateGroup(parents));
+    const auto hid = H5Dcreate2(static_cast<Derivate*>(this)->getId(),
+                                dataset_name.c_str(), dtype._hid, space._hid,
+                                lcpl.getId(), createProps.getId(), accessProps.getId());
+    if (hid < 0) {
+        HDF5ErrMapper::ToException<DataSetException>(
+            std::string("Unable to create the dataset \"") + dataset_name + "\":");
+    }
+    return DataSet(hid);
+}
+
+template <typename Derivate>
+template <typename Type>
+inline DataSet
+NodeTraits<Derivate>::createDataSet(const std::string& dataset_name,
+                                    const DataSpace& space,
+                                    const DataSetCreateProps& createProps,
+                                    const DataSetAccessProps& accessProps,
+                                    bool parents) {
+    return createDataSet(dataset_name, space,
+                         create_and_check_datatype<Type>(),
+                         createProps, accessProps, parents);
+}
+
+template <typename Derivate>
+template <typename T>
+inline DataSet
+NodeTraits<Derivate>::createDataSet(const std::string& dataset_name,
+                                    const T& data,
+                                    const DataSetCreateProps& createProps,
+                                    const DataSetAccessProps& accessProps,
+                                    bool parents) {
+    DataSet ds = createDataSet(
+        dataset_name, DataSpace::From(data),
+        create_and_check_datatype<typename details::inspector<T>::base_type>(),
+        createProps, accessProps, parents);
+    ds.write(data);
+    return ds;
+}
+
+template <typename Derivate>
+template <std::size_t N>
+inline DataSet
+NodeTraits<Derivate>::createDataSet(const std::string& dataset_name,
+                                    const FixedLenStringArray<N>& data,
+                                    const DataSetCreateProps& createProps,
+                                    const DataSetAccessProps& accessProps,
+                                    bool parents) {
+    DataSet ds = createDataSet<char[N]>(
+        dataset_name, DataSpace(data.size()), createProps, accessProps, parents);
+    ds.write(data);
+    return ds;
+}
+
+template <typename Derivate>
+inline DataSet
+NodeTraits<Derivate>::getDataSet(const std::string& dataset_name,
+                                 const DataSetAccessProps& accessProps) const {
+    const auto hid = H5Dopen2(static_cast<const Derivate*>(this)->getId(),
+                              dataset_name.c_str(), accessProps.getId());
+    if (hid < 0) {
+        HDF5ErrMapper::ToException<DataSetException>(
+            std::string("Unable to open the dataset \"") + dataset_name + "\":");
+    }
+    return DataSet(hid);
+}
+
+template <typename Derivate>
+inline Group NodeTraits<Derivate>::createGroup(const std::string& group_name,
+                                               bool parents) {
+
+    LinkCreateProps lcpl;
+    lcpl.add(CreateIntermediateGroup(parents));
+    const auto hid = H5Gcreate2(static_cast<Derivate*>(this)->getId(),
+                                group_name.c_str(), lcpl.getId(), H5P_DEFAULT, H5P_DEFAULT);
+    if (hid < 0) {
+        HDF5ErrMapper::ToException<GroupException>(
+            std::string("Unable to create the group \"") + group_name + "\":");
+    }
+    return Group(hid);
+}
+
+template <typename Derivate>
+inline Group
+NodeTraits<Derivate>::getGroup(const std::string& group_name) const {
+    const auto hid = H5Gopen2(static_cast<const Derivate*>(this)->getId(),
+                              group_name.c_str(), H5P_DEFAULT);
+    if (hid < 0) {
+        HDF5ErrMapper::ToException<GroupException>(
+            std::string("Unable to open the group \"") + group_name + "\":");
+    }
+    return Group(hid);
+}
+
+template <typename Derivate>
+inline size_t NodeTraits<Derivate>::getNumberObjects() const {
+    hsize_t res;
+    if (H5Gget_num_objs(static_cast<const Derivate*>(this)->getId(), &res) < 0) {
+        HDF5ErrMapper::ToException<GroupException>(
+            std::string("Unable to count objects in existing group or file"));
+    }
+    return static_cast<size_t>(res);
+}
+
+template <typename Derivate>
+inline std::string NodeTraits<Derivate>::getObjectName(size_t index) const {
+    return details::get_name([&](char* buffer, hsize_t length) {
+        return H5Lget_name_by_idx(
+                    static_cast<const Derivate*>(this)->getId(), ".", H5_INDEX_NAME, H5_ITER_INC,
+                    index, buffer, length, H5P_DEFAULT);
+    });
+}
+
+template <typename Derivate>
+inline bool NodeTraits<Derivate>::rename(const std::string& src_path,
+                                         const std::string& dst_path,
+                                         bool parents) const {
+    LinkCreateProps lcpl;
+    lcpl.add(CreateIntermediateGroup(parents));
+    herr_t status = H5Lmove(static_cast<const Derivate*>(this)->getId(), src_path.c_str(),
+                            static_cast<const Derivate*>(this)->getId(), dst_path.c_str(), lcpl.getId(), H5P_DEFAULT);
+    if (status < 0) {
+        HDF5ErrMapper::ToException<GroupException>(
+                    std::string("Unable to move link to \"") + dst_path + "\":");
+        return false;
+    }
+    return true;
+}
+
+template <typename Derivate>
+inline std::vector<std::string> NodeTraits<Derivate>::listObjectNames() const {
+
+    std::vector<std::string> names;
+    details::HighFiveIterateData iterateData(names);
+
+    size_t num_objs = getNumberObjects();
+    names.reserve(num_objs);
+
+    if (H5Literate(static_cast<const Derivate*>(this)->getId(), H5_INDEX_NAME,
+                   H5_ITER_INC, NULL,
+                   &details::internal_high_five_iterate<H5L_info_t>,
+                   static_cast<void*>(&iterateData)) < 0) {
+        HDF5ErrMapper::ToException<GroupException>(
+            std::string("Unable to list objects in group"));
+    }
+
+    return names;
+}
+
+template <typename Derivate>
+inline bool NodeTraits<Derivate>::_exist(const std::string& node_name,
+                                         bool raise_errors) const {
+    SilenceHDF5 silencer{};
+    const auto val = H5Lexists(static_cast<const Derivate*>(this)->getId(),
+                               node_name.c_str(), H5P_DEFAULT);
+    if (val < 0) {
+        if (raise_errors) {
+            HDF5ErrMapper::ToException<GroupException>("Invalid link for exist()");
+        } else {
+            return false;
+        }
+    }
+
+    // The root path always exists, but H5Lexists return 0 or 1
+    // depending of the version of HDF5, so always return true for it
+    // We had to call H5Lexists anyway to check that there are no errors
+    return (node_name == "/") ? true : (val > 0);
+}
+
+template <typename Derivate>
+inline bool NodeTraits<Derivate>::exist(const std::string& group_path) const {
+    // When there are slashes, first check everything is fine
+    // so that subsequent errors are only due to missing intermediate groups
+    if (group_path.find('/') != std::string::npos) {
+        _exist("/");  // Shall not throw under normal circumstances
+        // Unless "/" (already checked), verify path exists (not throwing errors)
+        return (group_path == "/") ? true : _exist(group_path, false);
+    }
+    return _exist(group_path);
+}
+
+
+template <typename Derivate>
+inline void NodeTraits<Derivate>::unlink(const std::string& node_name) const {
+    const herr_t val = H5Ldelete(static_cast<const Derivate*>(this)->getId(),
+                                 node_name.c_str(), H5P_DEFAULT);
+    if (val < 0) {
+        HDF5ErrMapper::ToException<GroupException>(
+            std::string("Invalid name for unlink() "));
+    }
+
+}
+
+
+
+// convert internal link types to enum class.
+// This function is internal, so H5L_TYPE_ERROR shall be handled in the calling context
+static inline LinkType _convert_link_type(const H5L_type_t& ltype) noexcept {
+    switch (ltype) {
+        case H5L_TYPE_HARD:
+            return LinkType::Hard;
+        case H5L_TYPE_SOFT:
+            return LinkType::Soft;
+        case H5L_TYPE_EXTERNAL:
+            return LinkType::External;
+        default:
+            // Other link types are possible but are considered strange to HighFive.
+            // see https://support.hdfgroup.org/HDF5/doc/RM/H5L/H5Lregister.htm
+            return LinkType::Other;
+    }
+}
+
+template <typename Derivate>
+inline LinkType NodeTraits<Derivate>::getLinkType(const std::string& node_name) const {
+    H5L_info_t linkinfo;
+    if (H5Lget_info(static_cast<const Derivate*>(this)->getId(),
+                    node_name.c_str(), &linkinfo, H5P_DEFAULT) < 0
+            || linkinfo.type == H5L_TYPE_ERROR) {
+        HDF5ErrMapper::ToException<GroupException>(
+            std::string("Unable to obtain info for link ") + node_name);
+    }
+    return _convert_link_type(linkinfo.type);
+}
+
+template <typename Derivate>
+inline ObjectType NodeTraits<Derivate>::getObjectType(const std::string& node_name) const {
+    return _open(node_name).getType();
+}
+
+
+template <typename Derivate>
+inline void NodeTraits<Derivate>::createSoftLink(const std::string& link_name,
+                                                 const std::string& obj_path,
+                                                 LinkCreateProps linkCreateProps,
+                                                 const LinkAccessProps& linkAccessProps,
+                                                 const bool parents) {
+    if (parents) {
+        linkCreateProps.add(CreateIntermediateGroup{});
+    }
+    auto status = H5Lcreate_soft(obj_path.c_str(),
+                                 static_cast<const Derivate*>(this)->getId(),
+                                 link_name.c_str(),
+                                 linkCreateProps.getId(), linkAccessProps.getId());
+    if (status < 0) {
+        HDF5ErrMapper::ToException<GroupException>(
+            std::string("Unable to create soft link: "));
+    }
+}
+
+
+template <typename Derivate>
+inline void NodeTraits<Derivate>::createExternalLink(const std::string& link_name,
+                                                     const std::string& h5_file,
+                                                     const std::string& obj_path,
+                                                     LinkCreateProps linkCreateProps,
+                                                     const LinkAccessProps& linkAccessProps,
+                                                     const bool parents) {
+    if (parents) {
+        linkCreateProps.add(CreateIntermediateGroup{});
+    }
+    auto status = H5Lcreate_external(h5_file.c_str(),
+                                     obj_path.c_str(),
+                                     static_cast<const Derivate*>(this)->getId(),
+                                     link_name.c_str(),
+                                     linkCreateProps.getId(), linkAccessProps.getId());
+    if (status < 0) {
+        HDF5ErrMapper::ToException<GroupException>(
+            std::string("Unable to create external link: "));
+    }
+}
+
+
+template <typename Derivate>
+inline Object NodeTraits<Derivate>::_open(const std::string& node_name,
+                                          const DataSetAccessProps& accessProps) const {
+   const auto id = H5Oopen(static_cast<const Derivate*>(this)->getId(),
+                           node_name.c_str(),
+                           accessProps.getId());
+    if (id < 0) {
+        HDF5ErrMapper::ToException<GroupException>(
+            std::string("Unable to open \"") + node_name + "\":");
+    }
+    return Object(id);
+}
+
+
+
+}  // namespace HighFive
+
+#endif  // H5NODE_TRAITS_MISC_HPP
diff --git a/vendor/highfive/include/highfive/bits/H5Object_misc.hpp b/vendor/highfive/include/highfive/bits/H5Object_misc.hpp
new file mode 100644
index 00000000..e57d9db7
--- /dev/null
+++ b/vendor/highfive/include/highfive/bits/H5Object_misc.hpp
@@ -0,0 +1,116 @@
+/*
+ *  Copyright (c), 2017, Adrien Devresse <adrien.devresse@epfl.ch>
+ *
+ *  Distributed under the Boost Software License, Version 1.0.
+ *    (See accompanying file LICENSE_1_0.txt or copy at
+ *          http://www.boost.org/LICENSE_1_0.txt)
+ *
+ */
+#ifndef H5OBJECT_MISC_HPP
+#define H5OBJECT_MISC_HPP
+
+#include <iostream>
+
+namespace HighFive {
+
+inline Object::Object() : _hid(H5I_INVALID_HID) {}
+
+inline Object::Object(hid_t hid) : _hid(hid) {}
+
+inline Object::Object(const Object& other) : _hid(other._hid) {
+    if (other.isValid() && H5Iinc_ref(_hid) < 0) {
+        throw ObjectException("Reference counter increase failure");
+    }
+}
+
+inline Object::Object(Object&& other) noexcept
+    : _hid(other._hid)  {
+    other._hid = H5I_INVALID_HID;
+}
+
+inline Object& Object::operator=(const Object& other) {
+    if (this != &other) {
+        if (_hid != H5I_INVALID_HID)
+            H5Idec_ref(_hid);
+
+        _hid = other._hid;
+        if (other.isValid() && H5Iinc_ref(_hid) < 0) {
+            throw ObjectException("Reference counter increase failure");
+        }
+    }
+    return *this;
+}
+
+inline Object::~Object() {
+    if (isValid() && H5Idec_ref(_hid) < 0) {
+        std::cerr << "HighFive::~Object: reference counter decrease failure"
+                  << std::endl;
+    }
+}
+
+inline bool Object::isValid() const noexcept {
+    return (_hid != H5I_INVALID_HID) && (H5Iis_valid(_hid) != false);
+}
+
+inline hid_t Object::getId() const noexcept {
+    return _hid;
+}
+
+static inline ObjectType _convert_object_type(const H5I_type_t& h5type) {
+    switch (h5type) {
+        case H5I_FILE:
+            return ObjectType::File;
+        case H5I_GROUP:
+            return ObjectType::Group;
+        case H5I_DATATYPE:
+            return ObjectType::UserDataType;
+        case H5I_DATASPACE:
+            return ObjectType::DataSpace;
+        case H5I_DATASET:
+            return ObjectType::Dataset;
+        case H5I_ATTR:
+            return ObjectType::Attribute;
+        default:
+            return ObjectType::Other;
+    }
+}
+
+inline ObjectType Object::getType() const {
+    // H5Iget_type is a very lightweight func which extracts the type from the id
+    H5I_type_t h5type;
+    if ((h5type = H5Iget_type(_hid)) == H5I_BADID) {
+        HDF5ErrMapper::ToException<ObjectException>("Invalid hid or object type");
+    }
+    return _convert_object_type(h5type);
+}
+
+inline ObjectInfo Object::getInfo() const {
+    ObjectInfo info;
+#if (H5Oget_info_vers < 3)
+    if (H5Oget_info(_hid, &info.raw_info) < 0) {
+#else
+    if (H5Oget_info1(_hid, &info.raw_info) < 0) {
+#endif
+        HDF5ErrMapper::ToException<ObjectException>("Unable to obtain info for object");
+    }
+    return info;
+}
+
+inline haddr_t ObjectInfo::getAddress() const noexcept {
+    return raw_info.addr;
+}
+inline size_t ObjectInfo::getRefCount() const noexcept {
+    return raw_info.rc;
+}
+inline time_t ObjectInfo::getCreationTime() const noexcept {
+    return raw_info.btime;
+}
+inline time_t ObjectInfo::getModificationTime() const noexcept {
+    return raw_info.mtime;
+}
+
+
+
+}  // namespace
+
+#endif // H5OBJECT_MISC_HPP
diff --git a/vendor/highfive/include/highfive/bits/H5Path_traits.hpp b/vendor/highfive/include/highfive/bits/H5Path_traits.hpp
new file mode 100644
index 00000000..b2271206
--- /dev/null
+++ b/vendor/highfive/include/highfive/bits/H5Path_traits.hpp
@@ -0,0 +1,37 @@
+/*
+ *  Copyright (c), 2020, EPFL - Blue Brain Project
+ *
+ *  Distributed under the Boost Software License, Version 1.0.
+ *    (See accompanying file LICENSE_1_0.txt or copy at
+ *          http://www.boost.org/LICENSE_1_0.txt)
+ *
+ */
+#pragma once
+
+#include "H5_definitions.hpp"
+
+namespace HighFive {
+
+template <typename Derivate>
+class PathTraits {
+
+public:
+    PathTraits();
+
+    ///
+    /// \brief return the path to the current object
+    /// \return the path to the object
+    std::string getPath() const;
+
+    ///
+    /// \brief Return a reference to the File object this object belongs
+    /// \return the File object ref
+    File& getFile() const noexcept;
+
+
+protected:
+    std::shared_ptr<File> _file_obj;  // keep a ref to file so we keep its ref count > 0
+
+};
+
+} // namespace HighFive
diff --git a/vendor/highfive/include/highfive/bits/H5Path_traits_misc.hpp b/vendor/highfive/include/highfive/bits/H5Path_traits_misc.hpp
new file mode 100644
index 00000000..75617a1f
--- /dev/null
+++ b/vendor/highfive/include/highfive/bits/H5Path_traits_misc.hpp
@@ -0,0 +1,48 @@
+/*
+ *  Copyright (c), 2020, EPFL - Blue Brain Project
+ *
+ *  Distributed under the Boost Software License, Version 1.0.
+ *    (See accompanying file LICENSE_1_0.txt or copy at
+ *          http://www.boost.org/LICENSE_1_0.txt)
+ *
+ */
+#pragma once
+
+#include <H5Ipublic.h>
+
+#include "H5Utils.hpp"
+#include "H5Path_traits.hpp"
+
+namespace HighFive{
+
+template <typename Derivate>
+inline PathTraits<Derivate>::PathTraits() {
+    static_assert(std::is_same<Derivate, Group>::value
+                  || std::is_same<Derivate, DataSet>::value
+                  || std::is_same<Derivate, Attribute>::value,
+                  "PathTraits can only be applied to Group, DataSet and Attribute");
+    const auto& obj = static_cast<const Derivate&>(*this);
+    if(!obj.isValid()) {
+        return;
+    }
+    const hid_t file_id = H5Iget_file_id(obj.getId());
+    if (file_id < 0) {
+        HDF5ErrMapper::ToException<PropertyException>(
+            "getFile(): Could not obtain file of object");
+    }
+    _file_obj.reset(new File(file_id));
+}
+
+template <typename Derivate>
+inline std::string PathTraits<Derivate>::getPath() const {
+    return details::get_name([this](char* buffer, hsize_t length) {
+        return H5Iget_name(static_cast<const Derivate*>(this)->getId(), buffer, length);
+    });
+}
+
+template <typename Derivate>
+inline File& PathTraits<Derivate>::getFile() const noexcept {
+    return *_file_obj;
+}
+
+}  // namespace HighFive
diff --git a/vendor/highfive/include/highfive/bits/H5PropertyList_misc.hpp b/vendor/highfive/include/highfive/bits/H5PropertyList_misc.hpp
new file mode 100644
index 00000000..aa1ccbf8
--- /dev/null
+++ b/vendor/highfive/include/highfive/bits/H5PropertyList_misc.hpp
@@ -0,0 +1,150 @@
+/*
+ *  Copyright (c), 2017-2018, Adrien Devresse <adrien.devresse@epfl.ch>
+ *                            Juan Hernando <juan.hernando@epfl.ch>
+ *  Distributed under the Boost Software License, Version 1.0.
+ *    (See accompanying file LICENSE_1_0.txt or copy at
+ *          http://www.boost.org/LICENSE_1_0.txt)
+ *
+ */
+#ifndef H5PROPERTY_LIST_MISC_HPP
+#define H5PROPERTY_LIST_MISC_HPP
+
+#include <H5Ppublic.h>
+
+namespace HighFive {
+
+namespace {
+inline hid_t convert_plist_type(PropertyType propertyType) {
+    // The HP5_XXX are macros with function calls so we can't assign
+    // them as the enum values
+    switch (propertyType) {
+    case PropertyType::OBJECT_CREATE:
+        return H5P_OBJECT_CREATE;
+    case PropertyType::FILE_CREATE:
+        return H5P_FILE_CREATE;
+    case PropertyType::FILE_ACCESS:
+        return H5P_FILE_ACCESS;
+    case PropertyType::DATASET_CREATE:
+        return H5P_DATASET_CREATE;
+    case PropertyType::DATASET_ACCESS:
+        return H5P_DATASET_ACCESS;
+    case PropertyType::DATASET_XFER:
+        return H5P_DATASET_XFER;
+    case PropertyType::GROUP_CREATE:
+        return H5P_GROUP_CREATE;
+    case PropertyType::GROUP_ACCESS:
+        return H5P_GROUP_ACCESS;
+    case PropertyType::DATATYPE_CREATE:
+        return H5P_DATATYPE_CREATE;
+    case PropertyType::DATATYPE_ACCESS:
+        return H5P_DATATYPE_ACCESS;
+    case PropertyType::STRING_CREATE:
+        return H5P_STRING_CREATE;
+    case PropertyType::ATTRIBUTE_CREATE:
+        return H5P_ATTRIBUTE_CREATE;
+    case PropertyType::OBJECT_COPY:
+        return H5P_OBJECT_COPY;
+    case PropertyType::LINK_CREATE:
+        return H5P_LINK_CREATE;
+    case PropertyType::LINK_ACCESS:
+        return H5P_LINK_ACCESS;
+    default:
+        HDF5ErrMapper::ToException<PropertyException>(
+            "Unsupported property list type");
+    }
+}
+
+}  // namespace
+
+
+inline PropertyListBase::PropertyListBase() noexcept
+    : Object(H5P_DEFAULT) {}
+
+
+template <PropertyType T>
+inline void PropertyList<T>::_initializeIfNeeded() {
+    if (_hid != H5P_DEFAULT) {
+        return;
+    }
+    if ((_hid = H5Pcreate(convert_plist_type(T))) < 0) {
+        HDF5ErrMapper::ToException<PropertyException>(
+            "Unable to create property list");
+    }
+}
+
+template <PropertyType T>
+template <typename P>
+inline void PropertyList<T>::add(const P& property) {
+    _initializeIfNeeded();
+    property.apply(_hid);
+}
+
+template <PropertyType T>
+template <typename F, typename... Args>
+inline void RawPropertyList<T>::add(const F& funct, const Args&... args) {
+    this->_initializeIfNeeded();
+    if (funct(this->_hid, args...) < 0) {
+        HDF5ErrMapper::ToException<PropertyException>(
+            "Error setting raw hdf5 property.");
+    }
+}
+
+
+// Specific options to be added to Property Lists
+
+inline void Chunking::apply(const hid_t hid) const {
+    if (H5Pset_chunk(hid, static_cast<int>(_dims.size()), _dims.data()) < 0) {
+        HDF5ErrMapper::ToException<PropertyException>(
+            "Error setting chunk property");
+    }
+}
+
+inline void Deflate::apply(const hid_t hid) const {
+    if (!H5Zfilter_avail(H5Z_FILTER_DEFLATE) ||
+            H5Pset_deflate(hid, _level) < 0) {
+        HDF5ErrMapper::ToException<PropertyException>(
+            "Error setting deflate property");
+    }
+}
+
+inline void Szip::apply(const hid_t hid) const {
+    if (!H5Zfilter_avail(H5Z_FILTER_SZIP)) {
+        HDF5ErrMapper::ToException<PropertyException>(
+            "Error setting szip property");
+    }
+
+    if (H5Pset_szip(hid, _options_mask, _pixels_per_block) < 0) {
+        HDF5ErrMapper::ToException<PropertyException>(
+            "Error setting szip property");
+    }
+}
+
+inline void Shuffle::apply(const hid_t hid) const {
+    if (!H5Zfilter_avail(H5Z_FILTER_SHUFFLE)) {
+        HDF5ErrMapper::ToException<PropertyException>(
+            "Error setting shuffle property");
+    }
+
+    if (H5Pset_shuffle(hid) < 0) {
+        HDF5ErrMapper::ToException<PropertyException>(
+            "Error setting shuffle property");
+    }
+}
+
+inline void Caching::apply(const hid_t hid) const {
+    if (H5Pset_chunk_cache(hid, _numSlots, _cacheSize, _w0) < 0) {
+        HDF5ErrMapper::ToException<PropertyException>(
+            "Error setting dataset cache parameters");
+    }
+}
+
+inline void CreateIntermediateGroup::apply(const hid_t hid) const {
+    if (H5Pset_create_intermediate_group(hid, _create ? 1 : 0) < 0) {
+        HDF5ErrMapper::ToException<PropertyException>(
+            "Error setting property for create intermediate groups");
+    }
+}
+
+}  // namespace HighFive
+
+#endif  // H5PROPERTY_LIST_HPP
diff --git a/vendor/highfive/include/highfive/bits/H5ReadWrite_misc.hpp b/vendor/highfive/include/highfive/bits/H5ReadWrite_misc.hpp
new file mode 100644
index 00000000..5e1383d3
--- /dev/null
+++ b/vendor/highfive/include/highfive/bits/H5ReadWrite_misc.hpp
@@ -0,0 +1,101 @@
+/*
+ *  Copyright (c) 2020 Blue Brain Project
+ *
+ *  Distributed under the Boost Software License, Version 1.0.
+ *    (See accompanying file LICENSE_1_0.txt or copy at
+ *          http://www.boost.org/LICENSE_1_0.txt)
+ *
+ */
+#pragma once
+
+#include "H5Tpublic.h"
+#include "H5Utils.hpp"
+
+namespace HighFive {
+
+namespace details {
+
+template <typename T>
+struct BufferInfo {
+    using type_no_const = typename std::remove_const<T>::type;
+    using elem_type = typename details::inspector<type_no_const>::base_type;
+    using char_array_t = typename details::type_char_array<type_no_const>::type;
+    static constexpr bool is_char_array = ! std::is_same<char_array_t, void>::value;
+
+    template <class F>
+    BufferInfo(const DataType& dtype, F getName);
+
+    // member data for info depending on the destination dataset type
+    const bool is_fixed_len_string;
+    const size_t n_dimensions;
+    const DataType data_type;
+};
+
+// details implementation
+template <typename SrcStrT>
+struct string_type_checker {
+    static DataType getDataType(const DataType&, const DataType&);
+};
+
+template <>
+struct string_type_checker<void> {
+inline static DataType getDataType(const DataType& element_type, const DataType& dtype) {
+    // TEMP. CHANGE: Ensure that the character set is properly configured to prevent
+    // converter issues on HDF5 <=v1.12.0 when loading ASCII strings first.
+    // See https://github.com/HDFGroup/hdf5/issues/544 for further information.
+    if (H5Tget_class(element_type.getId()) == H5T_STRING &&
+        H5Tget_cset(dtype.getId()) == H5T_CSET_ASCII) {
+        H5Tset_cset(element_type.getId(), H5T_CSET_ASCII);
+    }
+    return element_type;
+}};
+
+template <std::size_t FixedLen>
+struct string_type_checker<char[FixedLen]> {
+inline static DataType getDataType(const DataType& element_type, const DataType& dtype) {
+    DataType return_type = (dtype.isFixedLenStr()) ? AtomicType<char[FixedLen]>() : element_type;
+    // TEMP. CHANGE: See string_type_checker<void> definition
+    if (H5Tget_cset(dtype.getId()) == H5T_CSET_ASCII) {
+        H5Tset_cset(return_type.getId(), H5T_CSET_ASCII);
+    }
+    return return_type;
+}};
+
+template <>
+struct string_type_checker<char*> {
+inline static DataType getDataType(const DataType&, const DataType& dtype) {
+    if (dtype.isFixedLenStr())
+        throw DataSetException("Can't output variable-length to fixed-length strings");
+    // TEMP. CHANGE: See string_type_checker<void> definition
+    DataType return_type = AtomicType<std::string>();
+    if (H5Tget_cset(dtype.getId()) == H5T_CSET_ASCII) {
+        H5Tset_cset(return_type.getId(), H5T_CSET_ASCII);
+    }
+    return return_type;
+}};
+
+template <typename T>
+template <class F>
+BufferInfo<T>::BufferInfo(const DataType& dtype, F getName)
+    : is_fixed_len_string(dtype.isFixedLenStr())
+    // In case we are using Fixed-len strings we need to subtract one dimension
+    , n_dimensions(details::inspector<type_no_const>::recursive_ndim -
+                   ((is_fixed_len_string && is_char_array) ? 1 : 0))
+    , data_type(string_type_checker<char_array_t>::getDataType(
+            create_datatype<elem_type>(), dtype)) {
+    if (is_fixed_len_string && std::is_same<elem_type, std::string>::value) {
+        throw DataSetException("Can't output std::string as fixed-length. "
+                               "Use raw arrays or FixedLenStringArray");
+    }
+    // We warn. In case they are really not convertible an exception will rise on read/write
+    if (dtype.getClass() != data_type.getClass()) {
+        std::cerr << "HighFive WARNING \""
+                  << getName()
+                  << "\": data and hdf5 dataset have different types: "
+                  << data_type.string() << " -> " << dtype.string() << std::endl;
+    }
+}
+
+}  // namespace details
+
+}  // namespace HighFive
diff --git a/vendor/highfive/include/highfive/bits/H5Reference_misc.hpp b/vendor/highfive/include/highfive/bits/H5Reference_misc.hpp
new file mode 100644
index 00000000..b4aea3e7
--- /dev/null
+++ b/vendor/highfive/include/highfive/bits/H5Reference_misc.hpp
@@ -0,0 +1,69 @@
+/*
+ *  Copyright (c), 2020, EPFL - Blue Brain Project
+ *
+ *  Distributed under the Boost Software License, Version 1.0.
+ *    (See accompanying file LICENSE_1_0.txt or copy at
+ *          http://www.boost.org/LICENSE_1_0.txt)
+ *
+ */
+
+#ifndef H5REFERENCE_MISC_HPP
+#define H5REFERENCE_MISC_HPP
+
+#include <string>
+#include <H5Ppublic.h>
+
+#include "H5Utils.hpp"
+
+namespace HighFive {
+
+inline Reference::Reference(const Object& location, const Object& object)
+    : parent_id(location.getId()) {
+    obj_name = details::get_name([&](char *buffer, hsize_t length) {
+        return H5Iget_name(object.getId(), buffer, length); });
+}
+
+inline void Reference::create_ref(hobj_ref_t* refptr) const {
+    if (H5Rcreate(refptr, parent_id, obj_name.c_str(), H5R_OBJECT, -1) < 0) {
+        HDF5ErrMapper::ToException<ReferenceException>(
+            std::string("Unable to create the reference for \"") + obj_name + "\":");
+    }
+}
+
+inline ObjectType Reference::getType(const Object& location) const {
+    return get_ref(location).getType();
+}
+
+template <typename T>
+inline T Reference::dereference(const Object& location) const {
+    static_assert(std::is_same<DataSet, T>::value || std::is_same<Group, T>::value,
+                  "We can only (de)reference HighFive::Group or HighFive:DataSet");
+    auto obj = get_ref(location) ;
+    if (obj.getType() != T::type) {
+        HDF5ErrMapper::ToException<ReferenceException>(
+            "Trying to dereference the wrong type");
+    }
+#if defined __GNUC__ && __GNUC__ < 9
+    return std::move(obj);
+#else
+    return obj;
+#endif
+}
+
+inline Object Reference::get_ref(const Object& location) const {
+    hid_t res;
+#if (H5Rdereference_vers == 2)
+    if ((res = H5Rdereference(location.getId(), H5P_DEFAULT, H5R_OBJECT, &href)) < 0) {
+        HDF5ErrMapper::ToException<ReferenceException>("Unable to dereference.");
+    }
+#else
+    if ((res = H5Rdereference(location.getId(), H5R_OBJECT, &href)) < 0) {
+        HDF5ErrMapper::ToException<ReferenceException>("Unable to dereference.");
+    }
+#endif
+    return Object(res);
+}
+
+}  // namespace HighFive
+
+#endif  // H5REFERENCE_MISC_HPP
diff --git a/vendor/highfive/include/highfive/bits/H5Selection_misc.hpp b/vendor/highfive/include/highfive/bits/H5Selection_misc.hpp
new file mode 100644
index 00000000..2cb0213b
--- /dev/null
+++ b/vendor/highfive/include/highfive/bits/H5Selection_misc.hpp
@@ -0,0 +1,43 @@
+/*
+ *  Copyright (c), 2017, Adrien Devresse <adrien.devresse@epfl.ch>
+ *
+ *  Distributed under the Boost Software License, Version 1.0.
+ *    (See accompanying file LICENSE_1_0.txt or copy at
+ *          http://www.boost.org/LICENSE_1_0.txt)
+ *
+ */
+#ifndef H5SELECTION_MISC_HPP
+#define H5SELECTION_MISC_HPP
+
+namespace HighFive {
+
+inline Selection::Selection(const DataSpace& memspace,
+                            const DataSpace& file_space, const DataSet& set)
+    : _mem_space(memspace)
+    , _file_space(file_space)
+    , _set(set) {}
+
+inline DataSpace Selection::getSpace() const noexcept {
+    return _file_space;
+}
+
+inline DataSpace Selection::getMemSpace() const noexcept {
+    return _mem_space;
+}
+
+inline DataSet& Selection::getDataset() noexcept {
+    return _set;
+}
+
+inline const DataSet& Selection::getDataset() const noexcept {
+    return _set;
+}
+
+// Not only a shortcut but also for templated compat with H5Dataset
+inline const DataType Selection::getDataType() const {
+    return _set.getDataType();
+}
+
+}  // namespace HighFive
+
+#endif // H5SELECTION_MISC_HPP
diff --git a/vendor/highfive/include/highfive/bits/H5Slice_traits.hpp b/vendor/highfive/include/highfive/bits/H5Slice_traits.hpp
new file mode 100644
index 00000000..9c3c8163
--- /dev/null
+++ b/vendor/highfive/include/highfive/bits/H5Slice_traits.hpp
@@ -0,0 +1,128 @@
+/*
+ *  Copyright (c), 2017, Adrien Devresse <adrien.devresse@epfl.ch>
+ *
+ *  Distributed under the Boost Software License, Version 1.0.
+ *    (See accompanying file LICENSE_1_0.txt or copy at
+ *          http://www.boost.org/LICENSE_1_0.txt)
+ *
+ */
+#ifndef H5SLICE_TRAITS_HPP
+#define H5SLICE_TRAITS_HPP
+
+#include <cstdlib>
+#include <vector>
+
+#include "H5_definitions.hpp"
+#include "H5Utils.hpp"
+
+namespace HighFive {
+
+class ElementSet {
+  public:
+    ///
+    /// \brief Create a list of points of N-dimension for selection.
+    ///
+    /// \param list List of continuous coordinates (e.g.: in 2 dimensions space
+    /// `ElementSet{1, 2, 3 ,4}` creates points `(1, 2)` and `(3, 4)`).
+    explicit ElementSet(std::initializer_list<std::size_t> list);
+    ///
+    /// \brief Create a list of points of N-dimension for selection.
+    ///
+    /// \param list List of N-dim points.
+    explicit ElementSet(std::initializer_list<std::vector<std::size_t>> list);
+    ///
+    /// \brief Create a list of points of N-dimension for selection.
+    ///
+    /// \param element_ids List of continuous coordinates (e.g.: in 2 dimensions space
+    /// `ElementSet{1, 2, 3 ,4}` creates points `(1, 2)` and `(3, 4)`).
+    explicit ElementSet(const std::vector<std::size_t>& element_ids);
+    ///
+    /// \brief Create a list of points of N-dimension for selection.
+    ///
+    /// \param element_ids List of N-dim points.
+    explicit ElementSet(const std::vector<std::vector<std::size_t>>& element_ids);
+
+  private:
+    std::vector<std::size_t> _ids;
+
+    template <typename Derivate>
+    friend class SliceTraits;
+};
+
+
+template <typename Derivate>
+class SliceTraits {
+  public:
+    ///
+    /// \brief Select a region in the current Slice/Dataset of \p count points at
+    /// \p offset separated by \p stride. If strides are not provided they will
+    /// default to 1 in all dimensions.
+    ///
+    /// vector offset and count have to be from the same dimension
+    ///
+    Selection select(const std::vector<size_t>& offset,
+                     const std::vector<size_t>& count,
+                     const std::vector<size_t>& stride = std::vector<size_t>()) const;
+
+    ///
+    /// \brief Select a set of columns in the last dimension of this dataset.
+    ///
+    /// The column indices must be smaller than the dimension size.
+    ///
+    Selection select(const std::vector<size_t>& columns) const;
+
+    ///
+    /// \brief Select a region in the current Slice/Dataset out of a list of elements.
+    ///
+    Selection select(const ElementSet& elements) const;
+
+    ///
+    /// Read the entire dataset into a buffer
+    /// An exception is raised is if the numbers of dimension of the buffer and
+    /// of the dataset are different.
+    ///
+    /// The array type can be a N-pointer or a N-vector. For plain pointers
+    /// not dimensionality checking will be performed, it is the user's
+    /// responsibility to ensure that the right amount of space has been
+    /// allocated.
+    template <typename T>
+    void read(T& array) const;
+
+    ///
+    /// Read the entire dataset into a raw buffer
+    ///
+    /// No dimensionality checks will be performed, it is the user's
+    /// responsibility to ensure that the right amount of space has been
+    /// allocated.
+    /// \param array: A buffer containing enough space for the data
+    /// \param dtype: The type of the data, in case it cannot be automatically guessed
+    template <typename T>
+    void read(T* array, const DataType& dtype = DataType()) const;
+
+    ///
+    /// Write the integrality N-dimension buffer to this dataset
+    /// An exception is raised is if the numbers of dimension of the buffer and
+    /// of the dataset are different
+    ///
+    /// The array type can be a N-pointer or a N-vector ( e.g int** integer two
+    /// dimensional array )
+    template <typename T>
+    void write(const T& buffer);
+
+    ///
+    /// Write from a raw buffer into this dataset
+    ///
+    /// No dimensionality checks will be performed, it is the user's
+    /// responsibility to ensure that the buffer holds the right amount of
+    /// elements. For n-dimensional matrices the buffer layout follows H5
+    /// default conventions.
+    /// \param buffer: A buffer containing the data to be written
+    /// \param dtype: The type of the data, in case it cannot be automatically guessed
+    template <typename T>
+    void write_raw(const T* buffer, const DataType& dtype = DataType());
+
+};
+
+}  // namespace HighFive
+
+#endif // H5SLICE_TRAITS_HPP
diff --git a/vendor/highfive/include/highfive/bits/H5Slice_traits_misc.hpp b/vendor/highfive/include/highfive/bits/H5Slice_traits_misc.hpp
new file mode 100644
index 00000000..d5ae746f
--- /dev/null
+++ b/vendor/highfive/include/highfive/bits/H5Slice_traits_misc.hpp
@@ -0,0 +1,237 @@
+/*
+ *  Copyright (c), 2017, Adrien Devresse <adrien.devresse@epfl.ch>
+ *
+ *  Distributed under the Boost Software License, Version 1.0.
+ *    (See accompanying file LICENSE_1_0.txt or copy at
+ *          http://www.boost.org/LICENSE_1_0.txt)
+ *
+ */
+#ifndef H5SLICE_TRAITS_MISC_HPP
+#define H5SLICE_TRAITS_MISC_HPP
+
+#include <algorithm>
+#include <cassert>
+#include <functional>
+#include <numeric>
+#include <sstream>
+#include <string>
+
+#ifdef H5_USE_BOOST
+// starting Boost 1.64, serialization header must come before ublas
+#include <boost/multi_array.hpp>
+#include <boost/numeric/ublas/matrix.hpp>
+#include <boost/serialization/vector.hpp>
+#endif
+
+#include <H5Dpublic.h>
+#include <H5Ppublic.h>
+
+#include "H5ReadWrite_misc.hpp"
+#include "H5Converter_misc.hpp"
+
+namespace HighFive {
+
+namespace details {
+
+// map the correct reference to the dataset depending of the layout
+// dataset -> itself
+// subselection -> parent dataset
+inline const DataSet& get_dataset(const Selection& sel) {
+    return sel.getDataset();
+}
+
+inline const DataSet& get_dataset(const DataSet& ds) {
+    return ds;
+}
+
+// map the correct memspace identifier depending of the layout
+// dataset -> entire memspace
+// selection -> resolve space id
+inline hid_t get_memspace_id(const Selection& ptr) {
+    return ptr.getMemSpace().getId();
+}
+
+inline hid_t get_memspace_id(const DataSet&) {
+    return H5S_ALL;
+}
+}  // namespace details
+
+inline ElementSet::ElementSet(std::initializer_list<std::size_t> list)
+    : _ids(list) {}
+
+inline ElementSet::ElementSet(std::initializer_list<std::vector<std::size_t>> list)
+    : ElementSet(std::vector<std::vector<std::size_t>>(list)) {}
+
+inline ElementSet::ElementSet(const std::vector<std::size_t>& element_ids)
+    : _ids(element_ids) {}
+
+inline ElementSet::ElementSet(const std::vector<std::vector<std::size_t>>& element_ids) {
+    for (const auto& vec : element_ids) {
+        std::copy(vec.begin(), vec.end(), std::back_inserter(_ids));
+    }
+}
+
+template <typename Derivate>
+inline Selection SliceTraits<Derivate>::select(const std::vector<size_t>& offset,
+                                               const std::vector<size_t>& count,
+                                               const std::vector<size_t>& stride) const {
+    // hsize_t type conversion
+    // TODO : normalize hsize_t type in HighFive namespace
+    const auto& slice = static_cast<const Derivate&>(*this);
+    std::vector<hsize_t> offset_local(offset.size());
+    std::vector<hsize_t> count_local(count.size());
+    std::vector<hsize_t> stride_local(stride.size());
+    std::copy(offset.begin(), offset.end(), offset_local.begin());
+    std::copy(count.begin(), count.end(), count_local.begin());
+    std::copy(stride.begin(), stride.end(), stride_local.begin());
+
+    DataSpace space = slice.getSpace().clone();
+    if (H5Sselect_hyperslab(space.getId(), H5S_SELECT_SET, offset_local.data(),
+                            stride.empty() ? NULL : stride_local.data(),
+                            count_local.data(), NULL) < 0) {
+        HDF5ErrMapper::ToException<DataSpaceException>("Unable to select hyperslap");
+    }
+
+    return Selection(DataSpace(count), space, details::get_dataset(slice));
+}
+
+template <typename Derivate>
+inline Selection SliceTraits<Derivate>::select(const std::vector<size_t>& columns) const {
+    const auto& slice = static_cast<const Derivate&>(*this);
+    const DataSpace& space = slice.getSpace();
+    const DataSet& dataset = details::get_dataset(slice);
+    std::vector<size_t> dims = space.getDimensions();
+    std::vector<hsize_t> counts(dims.size());
+    std::copy(dims.begin(), dims.end(), counts.begin());
+    counts[dims.size() - 1] = 1;
+    std::vector<hsize_t> offsets(dims.size(), 0);
+
+    H5Sselect_none(space.getId());
+
+    for (const auto& column : columns) {
+        offsets[offsets.size() - 1] = column;
+
+        if (H5Sselect_hyperslab(space.getId(), H5S_SELECT_OR, offsets.data(), 0,
+                                counts.data(), 0) < 0) {
+            HDF5ErrMapper::ToException<DataSpaceException>("Unable to select hyperslap");
+        }
+    }
+
+    dims[dims.size() - 1] = columns.size();
+    return Selection(DataSpace(dims), space, dataset);
+}
+
+template <typename Derivate>
+inline Selection SliceTraits<Derivate>::select(const ElementSet& elements) const {
+    const auto& slice = static_cast<const Derivate&>(*this);
+    const hsize_t* data = nullptr;
+    const DataSpace space = slice.getSpace().clone();
+    const std::size_t length = elements._ids.size();
+    if (length % space.getNumberDimensions() != 0) {
+        throw DataSpaceException("Number of coordinates in elements picking "
+                                 "should be a multiple of the dimensions.");
+    }
+    const std::size_t num_elements = length / space.getNumberDimensions();
+    std::vector<hsize_t> raw_elements;
+
+    // optimised at compile time
+    // switch for data conversion on 32bits platforms
+    if (std::is_same<std::size_t, hsize_t>::value) {
+        // `if constexpr` can't be used, thus a reinterpret_cast is needed.
+        data = reinterpret_cast<const hsize_t*>(&(elements._ids[0]));
+    } else {
+        raw_elements.resize(length);
+        std::copy(elements._ids.begin(), elements._ids.end(), raw_elements.begin());
+        data = raw_elements.data();
+    }
+
+    if (H5Sselect_elements(space.getId(), H5S_SELECT_SET, num_elements, data) < 0) {
+        HDF5ErrMapper::ToException<DataSpaceException>("Unable to select elements");
+    }
+
+    return Selection(DataSpace(num_elements), space, details::get_dataset(slice));
+}
+
+
+template <typename Derivate>
+template <typename T>
+inline void SliceTraits<Derivate>::read(T& array) const {
+    const auto& slice = static_cast<const Derivate&>(*this);
+    const DataSpace& mem_space = slice.getMemSpace();
+    const details::BufferInfo<T> buffer_info(slice.getDataType(),
+        [slice]() -> std::string { return details::get_dataset(slice).getPath(); });
+
+    if (!details::checkDimensions(mem_space, buffer_info.n_dimensions)) {
+        std::ostringstream ss;
+        ss << "Impossible to read DataSet of dimensions "
+           << mem_space.getNumberDimensions() << " into arrays of dimensions "
+           << buffer_info.n_dimensions;
+        throw DataSpaceException(ss.str());
+    }
+    details::data_converter<T> converter(mem_space);
+    read(converter.transform_read(array), buffer_info.data_type);
+    // re-arrange results
+    converter.process_result(array);
+}
+
+
+template <typename Derivate>
+template <typename T>
+inline void SliceTraits<Derivate>::read(T* array, const DataType& dtype) const {
+    static_assert(!std::is_const<T>::value,
+                  "read() requires a non-const structure to read data into");
+    const auto& slice = static_cast<const Derivate&>(*this);
+    using element_type = typename details::inspector<T>::base_type;
+
+    // Auto-detect mem datatype if not provided
+    const DataType& mem_datatype =
+            dtype.empty() ? create_and_check_datatype<element_type>() : dtype;
+
+    if (H5Dread(details::get_dataset(slice).getId(),
+                mem_datatype.getId(),
+                details::get_memspace_id(slice),
+                slice.getSpace().getId(), H5P_DEFAULT, static_cast<void*>(array)) < 0) {
+        HDF5ErrMapper::ToException<DataSetException>("Error during HDF5 Read: ");
+    }
+}
+
+
+template <typename Derivate>
+template <typename T>
+inline void SliceTraits<Derivate>::write(const T& buffer) {
+    const auto& slice = static_cast<const Derivate&>(*this);
+    const DataSpace& mem_space = slice.getMemSpace();
+    const details::BufferInfo<T> buffer_info(slice.getDataType(),
+        [slice]() -> std::string { return details::get_dataset(slice).getPath(); });
+
+    if (!details::checkDimensions(mem_space, buffer_info.n_dimensions)) {
+        std::ostringstream ss;
+        ss << "Impossible to write buffer of dimensions " << buffer_info.n_dimensions
+           << " into dataset of dimensions " << mem_space.getNumberDimensions();
+        throw DataSpaceException(ss.str());
+    }
+    details::data_converter<T> converter(mem_space);
+    write_raw(converter.transform_write(buffer), buffer_info.data_type);
+}
+
+
+template <typename Derivate>
+template <typename T>
+inline void SliceTraits<Derivate>::write_raw(const T* buffer, const DataType& dtype) {
+    using element_type = typename details::inspector<T>::base_type;
+    const auto& slice = static_cast<const Derivate&>(*this);
+    const auto& mem_datatype =
+        dtype.empty() ? create_and_check_datatype<element_type>() : dtype;
+
+    if (H5Dwrite(details::get_dataset(slice).getId(),
+                 mem_datatype.getId(),
+                 details::get_memspace_id(slice),
+                 slice.getSpace().getId(), H5P_DEFAULT,
+                 static_cast<const void*>(buffer)) < 0) {
+        HDF5ErrMapper::ToException<DataSetException>("Error during HDF5 Write: ");
+    }
+}
+
+}  // namespace HighFive
+
+#endif  // H5SLICE_TRAITS_MISC_HPP
diff --git a/vendor/highfive/include/highfive/bits/H5Utils.hpp b/vendor/highfive/include/highfive/bits/H5Utils.hpp
new file mode 100644
index 00000000..92098a7b
--- /dev/null
+++ b/vendor/highfive/include/highfive/bits/H5Utils.hpp
@@ -0,0 +1,299 @@
+/*
+ *  Copyright (c), 2017, Adrien Devresse <adrien.devresse@epfl.ch>
+ *
+ *  Distributed under the Boost Software License, Version 1.0.
+ *    (See accompanying file LICENSE_1_0.txt or copy at
+ *          http://www.boost.org/LICENSE_1_0.txt)
+ *
+ */
+#ifndef H5UTILS_HPP
+#define H5UTILS_HPP
+
+// internal utilities functions
+#include <algorithm>
+#include <array>
+#include <cstddef> // __GLIBCXX__
+#include <exception>
+#include <string>
+#include <type_traits>
+#include <vector>
+
+#ifdef H5_USE_BOOST
+# include <boost/multi_array.hpp>
+# include <boost/numeric/ublas/matrix.hpp>
+#endif
+#ifdef H5_USE_EIGEN
+# include <Eigen/Eigen>
+#endif
+
+#include <H5public.h>
+
+#include "../H5Exception.hpp"
+
+namespace HighFive {
+
+// If ever used, recognize dimensions of FixedLenStringArray
+template <std::size_t N>
+class FixedLenStringArray;
+
+
+template <typename T>
+using unqualified_t = typename std::remove_const<typename std::remove_reference<T>::type
+        >::type;
+
+namespace details {
+template <typename T>
+struct inspector {
+    using type = T;
+    using base_type = unqualified_t<T>;
+
+    static constexpr size_t ndim = 0;
+    static constexpr size_t recursive_ndim = ndim;
+
+    static std::array<size_t, recursive_ndim> getDimensions(const type& /* val */) {
+        return std::array<size_t, recursive_ndim>();
+    }
+};
+
+template <size_t N>
+struct inspector<FixedLenStringArray<N>> {
+    using type = FixedLenStringArray<N>;
+    using base_type = FixedLenStringArray<N>;
+
+    static constexpr size_t ndim = 1;
+    static constexpr size_t recursive_ndim = ndim;
+
+    static std::array<size_t, recursive_ndim> getDimensions(const type& val) {
+        return std::array<size_t, recursive_ndim>{val.size()};
+    }
+};
+
+template <typename T>
+struct inspector<std::vector<T>> {
+    using type = std::vector<T>;
+    using value_type = T;
+    using base_type = typename inspector<value_type>::base_type;
+
+    static constexpr size_t ndim = 1;
+    static constexpr size_t recursive_ndim = ndim + inspector<value_type>::recursive_ndim;
+
+    static std::array<size_t, recursive_ndim> getDimensions(const type& val) {
+        std::array<size_t, recursive_ndim> sizes{val.size()};
+        size_t index = ndim;
+        for (const auto& s: inspector<value_type>::getDimensions(val[0])) {
+            sizes[index++] = s;
+        }
+        return sizes;
+    }
+};
+
+template <typename T>
+struct inspector<T*> {
+    using type = T*;
+    using value_type = T;
+    using base_type = typename inspector<value_type>::base_type;
+
+    static constexpr size_t ndim = 1;
+    static constexpr size_t recursive_ndim = ndim + inspector<value_type>::recursive_ndim;
+
+    static std::array<size_t, recursive_ndim> getDimensions(const type& /* val */) {
+        throw std::string("Not possible to have size of a T*");
+    }
+};
+
+template <typename T, size_t N>
+struct inspector<T[N]> {
+    using type = T[N];
+    using value_type = T;
+    using base_type = typename inspector<value_type>::base_type;
+
+    static constexpr size_t ndim = 1;
+    static constexpr size_t recursive_ndim = ndim + inspector<value_type>::recursive_ndim;
+
+    static std::array<size_t, recursive_ndim> getDimensions(const type& val) {
+        std::array<size_t, recursive_ndim> sizes{N};
+        size_t index = ndim;
+        for (const auto& s: inspector<value_type>::getDimensions(val[0])) {
+            sizes[index++] = s;
+        }
+        return sizes;
+    }
+};
+
+template <typename T, size_t N>
+struct inspector<std::array<T, N>> {
+    using type = std::array<T, N>;
+    using value_type = T;
+    using base_type = typename inspector<value_type>::base_type;
+
+    static constexpr size_t ndim = 1;
+    static constexpr size_t recursive_ndim = ndim + inspector<value_type>::recursive_ndim;
+
+    static std::array<size_t, recursive_ndim> getDimensions(const type& val) {
+        std::array<size_t, recursive_ndim> sizes{N};
+        size_t index = ndim;
+        for (const auto& s: inspector<value_type>::getDimensions(val[0])) {
+            sizes[index++] = s;
+        }
+        return sizes;
+    }
+};
+
+#ifdef H5_USE_EIGEN
+template <typename T, int M, int N>
+struct inspector<Eigen::Matrix<T, M, N>> {
+    using type = Eigen::Matrix<T, M, N>;
+    using value_type = T;
+    using base_type = typename inspector<value_type>::base_type;
+
+    static constexpr size_t ndim = 2;
+    static constexpr size_t recursive_ndim = ndim + inspector<value_type>::recursive_ndim;
+
+    static std::array<size_t, recursive_ndim> getDimensions(const type& val) {
+        std::array<size_t, recursive_ndim> sizes{static_cast<size_t>(val.rows()), static_cast<size_t>(val.cols())};
+        size_t index = ndim;
+        for (const auto& s: inspector<value_type>::getDimensions(val.data()[0])) {
+            sizes[index++] = s;
+        }
+        return sizes;
+    }
+};
+#endif
+
+#ifdef H5_USE_BOOST
+template <typename T, size_t Dims>
+struct inspector<boost::multi_array<T, Dims>> {
+    using type = boost::multi_array<T, Dims>;
+    using value_type = T;
+    using base_type = typename inspector<value_type>::base_type;
+
+    static constexpr size_t ndim = Dims;
+    static constexpr size_t recursive_ndim = ndim + inspector<value_type>::recursive_ndim;
+
+    static std::array<size_t, recursive_ndim> getDimensions(const type& val) {
+        std::array<size_t, recursive_ndim> sizes;
+        for (size_t i = 0; i < ndim; ++i) {
+            sizes[i] = val.shape()[i];
+        }
+
+        size_t index = ndim;
+        for (const auto& s: inspector<value_type>::getDimensions(val.data()[0])) {
+            sizes[index++] = s;
+        }
+        return sizes;
+    }
+};
+
+template <typename T>
+struct inspector<boost::numeric::ublas::matrix<T>> {
+    using type = boost::numeric::ublas::matrix<T>;
+    using value_type = T;
+    using base_type = typename inspector<value_type>::base_type;
+
+    static constexpr size_t ndim = 2;
+    static constexpr size_t recursive_ndim = ndim + inspector<value_type>::recursive_ndim;
+
+    static std::array<size_t, recursive_ndim> getDimensions(const type& val) {
+        std::array<size_t, recursive_ndim> sizes{val.size1(), val.size2()};
+        size_t index = ndim;
+        for (const auto& s: inspector<value_type>::getDimensions(val(0, 0))) {
+            sizes[index++] = s;
+        }
+        return sizes;
+    }
+};
+#endif
+
+
+// Find the type of an eventual char array, otherwise void
+template <typename>
+struct type_char_array {
+    typedef void type;
+};
+
+template <typename T>
+struct type_char_array<T*> {
+    typedef typename std::conditional<
+        std::is_same<unqualified_t<T>, char>::value,
+        char*,
+        typename type_char_array<T>::type
+    >::type type;
+};
+
+template <typename T, std::size_t N>
+struct type_char_array<T[N]> {
+    typedef typename std::conditional<
+        std::is_same<unqualified_t<T>, char>::value,
+        char[N],
+        typename type_char_array<T>::type
+    >::type type;
+};
+
+
+// check if the type is a container ( only vector supported for now )
+template <typename>
+struct is_container {
+    static const bool value = false;
+};
+
+template <typename T>
+struct is_container<std::vector<T> > {
+    static const bool value = true;
+};
+
+// check if the type is a basic C-Array
+template <typename>
+struct is_c_array {
+    static const bool value = false;
+};
+
+template <typename T>
+struct is_c_array<T*> {
+    static const bool value = true;
+};
+
+template <typename T, std::size_t N>
+struct is_c_array<T[N]> {
+    static const bool value = true;
+};
+
+
+// converter function for hsize_t -> size_t when hsize_t != size_t
+template <typename Size>
+inline std::vector<std::size_t> to_vector_size_t(const std::vector<Size>& vec) {
+    static_assert(std::is_same<Size, std::size_t>::value == false,
+                  " hsize_t != size_t mandatory here");
+    std::vector<size_t> res(vec.size());
+    std::transform(vec.cbegin(), vec.cend(), res.begin(), [](Size e) {
+        return static_cast<size_t>(e);
+    });
+    return res;
+}
+
+// converter function for hsize_t -> size_t when size_t == hsize_t
+inline std::vector<std::size_t> to_vector_size_t(const std::vector<std::size_t>& vec) {
+    return vec;
+}
+
+// read name from a H5 object using the specified function
+template<typename T>
+inline std::string get_name(T fct) {
+    const size_t maxLength = 255;
+    char buffer[maxLength + 1];
+    ssize_t retcode = fct(buffer, static_cast<hsize_t>(maxLength) + 1);
+    if (retcode < 0) {
+        HDF5ErrMapper::ToException<GroupException>("Error accessing object name");
+    }
+    const size_t length = static_cast<std::size_t>(retcode);
+    if (length <= maxLength) {
+        return std::string(buffer, length);
+    }
+    std::vector<char> bigBuffer(length + 1, 0);
+    fct(bigBuffer.data(), static_cast<hsize_t>(length) + 1);
+    return std::string(bigBuffer.data(), length);
+}
+
+}  // namespace details
+}  // namespace HighFive
+
+#endif // H5UTILS_HPP
diff --git a/vendor/highfive/include/highfive/bits/H5_definitions.hpp b/vendor/highfive/include/highfive/bits/H5_definitions.hpp
new file mode 100644
index 00000000..3d06ca86
--- /dev/null
+++ b/vendor/highfive/include/highfive/bits/H5_definitions.hpp
@@ -0,0 +1,62 @@
+#pragma once
+
+#if defined(__GNUC__) || defined(__clang__)
+#define H5_DEPRECATED(msg) __attribute__((deprecated(#msg)))
+#elif defined(_MSC_VER)
+#define H5_DEPRECATED(msg) __declspec(deprecated(#msg))
+#else
+#pragma message("WARNING: Compiler doesnt support deprecation")
+#define H5_DEPRECATED
+#endif
+
+
+// Forward declarations
+
+namespace HighFive {
+
+enum class LinkType;
+enum class ObjectType;
+enum class PropertyType;
+
+class Attribute;
+class DataSet;
+class DataSpace;
+class DataType;
+class Exception;
+class File;
+class FileDriver;
+class Group;
+class Object;
+class ObjectInfo;
+class Reference;
+class Selection;
+class SilenceHDF5;
+
+template <typename T>
+class AtomicType;
+
+template <typename Derivate>
+class AnnotateTraits;
+
+template <std::size_t N>
+class FixedLenStringArray;
+
+template <typename Derivate>
+class NodeTraits;
+
+template <PropertyType T>
+class PropertyList;
+
+
+// Internal
+
+namespace details {
+
+// Forward declaration of data_converter with default value of Enable
+template <typename T, typename Enable = void>
+struct data_converter;
+
+}
+
+}  // namespace HighFive
+
diff --git a/vendor/highfive/include/highfive/h5easy_bits/H5Easy_Eigen.hpp b/vendor/highfive/include/highfive/h5easy_bits/H5Easy_Eigen.hpp
new file mode 100644
index 00000000..2a72057d
--- /dev/null
+++ b/vendor/highfive/include/highfive/h5easy_bits/H5Easy_Eigen.hpp
@@ -0,0 +1,151 @@
+/*
+ *  Copyright (c), 2017, Adrien Devresse <adrien.devresse@epfl.ch>
+ *
+ *  Distributed under the Boost Software License, Version 1.0.
+ *    (See accompanying file LICENSE_1_0.txt or copy at
+ *          http://www.boost.org/LICENSE_1_0.txt)
+ *
+ */
+#ifndef H5EASY_BITS_EIGEN_HPP
+#define H5EASY_BITS_EIGEN_HPP
+
+#include "../H5Easy.hpp"
+#include "H5Easy_misc.hpp"
+#include "H5Easy_scalar.hpp"
+
+#ifdef H5_USE_EIGEN
+
+namespace H5Easy {
+
+namespace detail {
+
+template <typename T>
+struct io_impl<
+    T,
+    typename std::enable_if<std::is_base_of<Eigen::DenseBase<T>, T>::value>::type> {
+
+    // abbreviate row-major <-> col-major conversions
+    template <typename S>
+    struct types
+    {
+        using row_major = Eigen::Ref<
+            const Eigen::Array<
+                typename std::decay<T>::type::Scalar,
+                std::decay<T>::type::RowsAtCompileTime,
+                std::decay<T>::type::ColsAtCompileTime,
+                std::decay<T>::type::ColsAtCompileTime == 1 ? Eigen::ColMajor : Eigen::RowMajor,
+                std::decay<T>::type::MaxRowsAtCompileTime,
+                std::decay<T>::type::MaxColsAtCompileTime>,
+            0,
+            Eigen::InnerStride<1>>;
+
+        using col_major = Eigen::Map<
+            Eigen::Array<
+                typename std::decay<T>::type::Scalar,
+                std::decay<T>::type::RowsAtCompileTime,
+                std::decay<T>::type::ColsAtCompileTime,
+                std::decay<T>::type::ColsAtCompileTime == 1 ? Eigen::ColMajor : Eigen::RowMajor,
+                std::decay<T>::type::MaxRowsAtCompileTime,
+                std::decay<T>::type::MaxColsAtCompileTime>>;
+    };
+
+    // return the shape of Eigen::DenseBase<T> object as size 1 or 2 "std::vector<size_t>"
+    inline static std::vector<size_t> shape(const T& data) {
+        if (std::decay<T>::type::RowsAtCompileTime == 1) {
+            return {static_cast<size_t>(data.cols())};
+        }
+        if (std::decay<T>::type::ColsAtCompileTime == 1) {
+            return {static_cast<size_t>(data.rows())};
+        }
+        return {static_cast<size_t>(data.rows()), static_cast<size_t>(data.cols())};
+    }
+
+    using EigenIndex = Eigen::DenseIndex;
+
+    // get the shape of a "DataSet" as size 2 "std::vector<Eigen::Index>"
+    template <class D>
+    inline static std::vector<EigenIndex> shape(const File& file,
+                                                const std::string& path,
+                                                const D& dataset,
+                                                int RowsAtCompileTime) {
+        std::vector<size_t> dims = dataset.getDimensions();
+
+        if (dims.size() == 1 && RowsAtCompileTime == 1) {
+            return std::vector<EigenIndex>{1u, static_cast<EigenIndex>(dims[0])};
+        }
+        if (dims.size() == 1) {
+            return std::vector<EigenIndex>{static_cast<EigenIndex>(dims[0]), 1u};
+        }
+        if (dims.size() == 2) {
+            return std::vector<EigenIndex>{static_cast<EigenIndex>(dims[0]),
+                                           static_cast<EigenIndex>(dims[1])};
+        }
+
+        throw detail::error(file, path, "H5Easy::load: Inconsistent rank");
+    }
+
+    inline static DataSet dump(File& file,
+                               const std::string& path,
+                               const T& data,
+                               const DumpOptions& options) {
+        using row_major_type = typename types<T>::row_major;
+        using value_type = typename std::decay<T>::type::Scalar;
+        row_major_type row_major(data);
+        DataSet dataset = initDataset<value_type>(file, path, shape(data), options);
+        dataset.write_raw(row_major.data());
+        if (options.flush()) {
+            file.flush();
+        }
+        return dataset;
+    }
+
+    inline static T load(const File& file, const std::string& path) {
+        DataSet dataset = file.getDataSet(path);
+        std::vector<typename T::Index> dims = shape(file, path, dataset, T::RowsAtCompileTime);
+        T data(dims[0], dims[1]);
+        dataset.read(data.data());
+        if (data.IsVectorAtCompileTime || data.IsRowMajor) {
+            return data;
+        }
+        using col_major = typename types<T>::col_major;
+        return col_major(data.data(), dims[0], dims[1]);
+    }
+
+    inline static Attribute dumpAttribute(File& file,
+                                          const std::string& path,
+                                          const std::string& key,
+                                          const T& data,
+                                          const DumpOptions& options) {
+        using row_major_type = typename types<T>::row_major;
+        using value_type = typename std::decay<T>::type::Scalar;
+        row_major_type row_major(data);
+        Attribute attribute = initAttribute<value_type>(file, path, key, shape(data), options);
+        attribute.write_raw(row_major.data());
+        if (options.flush()) {
+            file.flush();
+        }
+        return attribute;
+    }
+
+    inline static T loadAttribute(const File& file,
+                                  const std::string& path,
+                                  const std::string& key) {
+        DataSet dataset = file.getDataSet(path);
+        Attribute attribute = dataset.getAttribute(key);
+        DataSpace dataspace = attribute.getSpace();
+        std::vector<typename T::Index> dims = shape(file, path, dataspace, T::RowsAtCompileTime);
+        T data(dims[0], dims[1]);
+        attribute.read(data.data());
+        if (data.IsVectorAtCompileTime || data.IsRowMajor) {
+            return data;
+        }
+        using col_major = typename types<T>::col_major;
+        return col_major(data.data(), dims[0], dims[1]);
+    }
+};
+
+}  // namespace detail
+}  // namespace H5Easy
+
+#endif  // H5_USE_EIGEN
+#endif  // H5EASY_BITS_EIGEN_HPP
diff --git a/vendor/highfive/include/highfive/h5easy_bits/H5Easy_misc.hpp b/vendor/highfive/include/highfive/h5easy_bits/H5Easy_misc.hpp
new file mode 100644
index 00000000..d7612bc6
--- /dev/null
+++ b/vendor/highfive/include/highfive/h5easy_bits/H5Easy_misc.hpp
@@ -0,0 +1,158 @@
+/*
+ *  Copyright (c), 2017, Adrien Devresse <adrien.devresse@epfl.ch>
+ *
+ *  Distributed under the Boost Software License, Version 1.0.
+ *    (See accompanying file LICENSE_1_0.txt or copy at
+ *          http://www.boost.org/LICENSE_1_0.txt)
+ *
+ */
+#ifndef H5EASY_BITS_MISC_HPP
+#define H5EASY_BITS_MISC_HPP
+
+#include "../H5Easy.hpp"
+
+namespace H5Easy {
+
+namespace detail {
+
+// Generate error-stream and return "Exception" (not yet thrown).
+inline Exception error(const File& file,
+                       const std::string& path,
+                       const std::string& message) {
+    std::ostringstream ss;
+    ss << message << std::endl
+       << "Path: " << path << std::endl
+       << "Filename: " << file.getName() << std::endl;
+    return Exception(ss.str());
+}
+
+// Generate specific dump error
+inline Exception dump_error(File& file, const std::string& path)
+{
+    if (file.getObjectType(path) == ObjectType::Dataset) {
+        return error(file, path,
+            "H5Easy: Dataset already exists, dump with H5Easy::DumpMode::Overwrite "
+            "to overwrite (with an array of the same shape).");
+    } else {
+        return error(file, path,
+            "H5Easy: path exists, but does not correspond to a Dataset. Dump not possible.");
+    }
+}
+
+// get a opened DataSet: nd-array
+template <class T>
+inline DataSet initDataset(File& file,
+                           const std::string& path,
+                           const std::vector<size_t>& shape,
+                           const DumpOptions& options)
+{
+    if (!file.exist(path)) {
+        if (!options.compress() && !options.isChunked()) {
+            return file.createDataSet<T>(path, DataSpace(shape), {}, {}, true);
+        } else {
+            std::vector<hsize_t> chunks(shape.begin(), shape.end());
+            if (options.isChunked()) {
+                chunks = options.getChunkSize();
+                if (chunks.size() != shape.size()) {
+                    throw error(file, path, "H5Easy::dump: Incorrect rank ChunkSize");
+                }
+            }
+            DataSetCreateProps props;
+            props.add(Chunking(chunks));
+            if (options.compress()) {
+                props.add(Shuffle());
+                props.add(Deflate(options.getCompressionLevel()));
+            }
+            return file.createDataSet<T>(path, DataSpace(shape), props, {}, true);
+        }
+    } else if (options.overwrite() && file.getObjectType(path) == ObjectType::Dataset) {
+        DataSet dataset = file.getDataSet(path);
+        if (dataset.getDimensions() != shape) {
+            throw error(file, path, "H5Easy::dump: Inconsistent dimensions");
+        }
+        return dataset;
+    }
+    throw dump_error(file, path);
+}
+
+// get a opened DataSet: scalar
+template <class T>
+inline DataSet initScalarDataset(File& file,
+                                 const std::string& path,
+                                 const T& data,
+                                 const DumpOptions& options)
+{
+    if (!file.exist(path)) {
+        return file.createDataSet<T>(path, DataSpace::From(data), {}, {}, true);
+    } else if (options.overwrite() && file.getObjectType(path) == ObjectType::Dataset) {
+        DataSet dataset = file.getDataSet(path);
+        if (dataset.getElementCount() != 1) {
+            throw error(file, path, "H5Easy::dump: Existing field not a scalar");
+        }
+        return dataset;
+    }
+    throw dump_error(file, path);
+}
+
+// get a opened Attribute: nd-array
+template <class T>
+inline Attribute initAttribute(File& file,
+                               const std::string& path,
+                               const std::string& key,
+                               const std::vector<size_t>& shape,
+                               const DumpOptions& options)
+{
+    if (!file.exist(path)) {
+        throw error(file, path, "H5Easy::dumpAttribute: DataSet does not exist");
+    }
+    if (file.getObjectType(path) != ObjectType::Dataset) {
+        throw error(file, path, "H5Easy::dumpAttribute: path not a DataSet");
+    }
+    DataSet dataset = file.getDataSet(path);
+    if (!dataset.hasAttribute(key)) {
+        return dataset.createAttribute<T>(key, DataSpace(shape));
+    } else if (options.overwrite()) {
+        Attribute attribute = dataset.getAttribute(key);
+        DataSpace dataspace = attribute.getSpace();
+        if (dataspace.getDimensions() != shape) {
+            throw error(file, path, "H5Easy::dumpAttribute: Inconsistent dimensions");
+        }
+        return attribute;
+    }
+    throw error(file, path,
+        "H5Easy: Attribute exists, overwrite with H5Easy::DumpMode::Overwrite.");
+}
+
+// get a opened Attribute: scalar
+template <class T>
+inline Attribute initScalarAttribute(File& file,
+                                     const std::string& path,
+                                     const std::string& key,
+                                     const T& data,
+                                     const DumpOptions& options)
+{
+    if (!file.exist(path)) {
+        throw error(file, path, "H5Easy::dumpAttribute: DataSet does not exist");
+    }
+    if (file.getObjectType(path) != ObjectType::Dataset) {
+        throw error(file, path, "H5Easy::dumpAttribute: path not a DataSet");
+    }
+    DataSet dataset = file.getDataSet(path);
+    if (!dataset.hasAttribute(key)) {
+        return dataset.createAttribute<T>(key, DataSpace::From(data));
+    } else if (options.overwrite()) {
+        Attribute attribute = dataset.getAttribute(key);
+        DataSpace dataspace = attribute.getSpace();
+        if (dataspace.getElementCount() != 1) {
+            throw error(file, path, "H5Easy::dumpAttribute: Existing field not a scalar");
+        }
+        return attribute;
+    }
+    throw error(file, path,
+        "H5Easy: Attribute exists, overwrite with H5Easy::DumpMode::Overwrite.");
+}
+
+}  // namespace detail
+}  // namespace H5Easy
+
+#endif  // H5EASY_BITS_MISC_HPP
diff --git a/vendor/highfive/include/highfive/h5easy_bits/H5Easy_opencv.hpp b/vendor/highfive/include/highfive/h5easy_bits/H5Easy_opencv.hpp
new file mode 100644
index 00000000..0e8d9a6e
--- /dev/null
+++ b/vendor/highfive/include/highfive/h5easy_bits/H5Easy_opencv.hpp
@@ -0,0 +1,107 @@
+/*
+ *  Copyright (c), 2017, Adrien Devresse <adrien.devresse@epfl.ch>
+ *
+ *  Distributed under the Boost Software License, Version 1.0.
+ *    (See accompanying file LICENSE_1_0.txt or copy at
+ *          http://www.boost.org/LICENSE_1_0.txt)
+ *
+ */
+#ifndef H5EASY_BITS_OPENCV_HPP
+#define H5EASY_BITS_OPENCV_HPP
+
+#include "../H5Easy.hpp"
+#include "H5Easy_misc.hpp"
+#include "H5Easy_scalar.hpp"
+
+#ifdef H5_USE_OPENCV
+
+namespace H5Easy {
+
+namespace detail {
+
+template <class T>
+struct is_opencv : std::false_type {};
+template <class T>
+struct is_opencv<cv::Mat_<T>> : std::true_type {};
+
+template <typename T>
+struct io_impl<T, typename std::enable_if<is_opencv<T>::value>::type> {
+
+    inline static std::vector<size_t> shape(const T& data)
+    {
+        return std::vector<size_t>{static_cast<size_t>(data.rows),
+                                   static_cast<size_t>(data.cols)};
+    }
+
+    inline static std::vector<int> shape(const File& file,
+                                         const std::string& path,
+                                         std::vector<size_t> dims)
+    {
+        if (dims.size() == 1) {
+            return std::vector<int>{static_cast<int>(dims[0]), 1ul};
+        }
+        if (dims.size() == 2) {
+            return std::vector<int>{static_cast<int>(dims[0]),
+                                    static_cast<int>(dims[1])};
+        }
+
+        throw detail::error(file, path, "H5Easy::load: Inconsistent rank");
+    }
+
+    inline static DataSet dump(File& file,
+                               const std::string& path,
+                               const T& data,
+                               const DumpOptions& options) {
+        using value_type = typename T::value_type;
+        DataSet dataset = initDataset<value_type>(file, path, shape(data), options);
+        std::vector<value_type> v(data.begin(), data.end());
+        dataset.write_raw(v.data());
+        if (options.flush()) {
+            file.flush();
+        }
+        return dataset;
+    }
+
+    inline static T load(const File& file, const std::string& path) {
+        using value_type = typename T::value_type;
+        DataSet dataset = file.getDataSet(path);
+        std::vector<int> dims = shape(file, path, dataset.getDimensions());
+        T data(dims[0], dims[1]);
+        dataset.read(reinterpret_cast<value_type*>(data.data));
+        return data;
+    }
+
+    inline static Attribute dumpAttribute(File& file,
+                                          const std::string& path,
+                                          const std::string& key,
+                                          const T& data,
+                                          const DumpOptions& options) {
+        using value_type = typename T::value_type;
+        Attribute attribute = initAttribute<value_type>(file, path, key, shape(data), options);
+        std::vector<value_type> v(data.begin(), data.end());
+        attribute.write_raw(v.data());
+        if (options.flush()) {
+            file.flush();
+        }
+        return attribute;
+    }
+
+    inline static T loadAttribute(const File& file,
+                                  const std::string& path,
+                                  const std::string& key) {
+        using value_type = typename T::value_type;
+        DataSet dataset = file.getDataSet(path);
+        Attribute attribute = dataset.getAttribute(key);
+        DataSpace dataspace = attribute.getSpace();
+        std::vector<int> dims = shape(file, path, dataspace.getDimensions());
+        T data(dims[0], dims[1]);
+        attribute.read(reinterpret_cast<value_type*>(data.data));
+        return data;
+    }
+};
+
+}  // namespace detail
+}  // namespace H5Easy
+
+#endif  // H5_USE_OPENCV
+#endif  // H5EASY_BITS_OPENCV_HPP
diff --git a/vendor/highfive/include/highfive/h5easy_bits/H5Easy_public.hpp b/vendor/highfive/include/highfive/h5easy_bits/H5Easy_public.hpp
new file mode 100644
index 00000000..c12037dc
--- /dev/null
+++ b/vendor/highfive/include/highfive/h5easy_bits/H5Easy_public.hpp
@@ -0,0 +1,191 @@
+/*
+ *  Copyright (c), 2017, Adrien Devresse <adrien.devresse@epfl.ch>
+ *
+ *  Distributed under the Boost Software License, Version 1.0.
+ *    (See accompanying file LICENSE_1_0.txt or copy at
+ *          http://www.boost.org/LICENSE_1_0.txt)
+ *
+ */
+#ifndef H5EASY_BITS_PUBLIC_HPP
+#define H5EASY_BITS_PUBLIC_HPP
+
+#include "../H5Easy.hpp"
+
+namespace H5Easy {
+
+inline Compression::Compression(bool enable)
+{
+    if (enable) {
+        m_compression_level = 9;
+    } else {
+        m_compression_level = 0;
+    }
+}
+
+template <class T>
+inline Compression::Compression(T level) : m_compression_level(static_cast<unsigned>(level))
+{
+}
+
+inline unsigned Compression::get() const
+{
+    return m_compression_level;
+}
+
+inline void DumpOptions::set(DumpMode mode)
+{
+    m_overwrite = static_cast<bool>(mode);
+}
+
+inline void DumpOptions::set(Flush mode)
+{
+    m_flush = static_cast<bool>(mode);
+}
+
+inline void DumpOptions::set(const Compression& level)
+{
+    m_compression_level = level.get();
+}
+
+template <class T, class... Args>
+inline void DumpOptions::set(T arg, Args... args)
+{
+    set(arg);
+    set(args...);
+}
+
+template <class T>
+inline void DumpOptions::setChunkSize(const std::vector<T>& shape)
+{
+    m_chunk_size = std::vector<hsize_t>(shape.begin(), shape.end());
+}
+
+inline void DumpOptions::setChunkSize(std::initializer_list<size_t> shape)
+{
+    m_chunk_size = std::vector<hsize_t>(shape.begin(), shape.end());
+}
+
+inline bool DumpOptions::overwrite() const
+{
+    return m_overwrite;
+}
+
+inline bool DumpOptions::flush() const
+{
+    return m_flush;
+}
+
+inline bool DumpOptions::compress() const
+{
+    return m_compression_level > 0;
+}
+
+inline unsigned DumpOptions::getCompressionLevel() const
+{
+    return m_compression_level;
+}
+
+inline bool DumpOptions::isChunked() const
+{
+    return m_chunk_size.size() > 0;
+}
+
+inline std::vector<hsize_t> DumpOptions::getChunkSize() const
+{
+    return m_chunk_size;
+}
+
+inline size_t getSize(const File& file, const std::string& path) {
+    return file.getDataSet(path).getElementCount();
+}
+
+inline std::vector<size_t> getShape(const File& file, const std::string& path) {
+    return file.getDataSet(path).getDimensions();
+}
+
+template <class T>
+inline DataSet dump(File& file,
+                    const std::string& path,
+                    const T& data,
+                    const DumpOptions& options) {
+    return detail::io_impl<T>::dump(file, path, data, options);
+}
+
+template <class T>
+inline DataSet dump(File& file,
+                    const std::string& path,
+                    const T& data,
+                    DumpMode mode) {
+    return detail::io_impl<T>::dump(file, path, data, DumpOptions(mode));
+}
+
+template <class T>
+inline DataSet dump(File& file,
+                    const std::string& path,
+                    const T& data,
+                    const std::vector<size_t>& idx,
+                    const DumpOptions& options) {
+    return detail::io_impl<T>::dump_extend(file, path, data, idx, options);
+}
+
+template <class T>
+inline DataSet dump(File& file,
+                    const std::string& path,
+                    const T& data,
+                    const std::initializer_list<size_t>& idx,
+                    const DumpOptions& options) {
+    return detail::io_impl<T>::dump_extend(file, path, data, idx, options);
+}
+
+template <class T>
+inline DataSet dump(File& file,
+                    const std::string& path,
+                    const T& data,
+                    const std::vector<size_t>& idx) {
+    return detail::io_impl<T>::dump_extend(file, path, data, idx, DumpOptions());
+}
+
+template <class T>
+inline DataSet dump(File& file,
+                    const std::string& path,
+                    const T& data,
+                    const std::initializer_list<size_t>& idx) {
+    return detail::io_impl<T>::dump_extend(file, path, data, idx, DumpOptions());
+}
+
+template <class T>
+inline T load(const File& file, const std::string& path, const std::vector<size_t>& idx) {
+    return detail::io_impl<T>::load_part(file, path, idx);
+}
+
+template <class T>
+inline T load(const File& file, const std::string& path) {
+    return detail::io_impl<T>::load(file, path);
+}
+
+template <class T>
+inline Attribute dumpAttribute(File& file,
+                               const std::string& path,
+                               const std::string& key,
+                               const T& data,
+                               DumpMode mode) {
+    return detail::io_impl<T>::dumpAttribute(file, path, key, data, DumpOptions(mode));
+}
+
+template <class T>
+inline Attribute dumpAttribute(File& file,
+                               const std::string& path,
+                               const std::string& key,
+                               const T& data,
+                               const DumpOptions& options) {
+    return detail::io_impl<T>::dumpAttribute(file, path, key, data, options);
+}
+
+template <class T>
+inline T loadAttribute(const File& file, const std::string& path, const std::string& key) {
+    return detail::io_impl<T>::loadAttribute(file, path, key);
+}
+
+}  // namespace H5Easy
+
+#endif  // H5EASY_BITS_MISC_HPP
diff --git a/vendor/highfive/include/highfive/h5easy_bits/H5Easy_scalar.hpp b/vendor/highfive/include/highfive/h5easy_bits/H5Easy_scalar.hpp
new file mode 100644
index 00000000..60a1bd56
--- /dev/null
+++ b/vendor/highfive/include/highfive/h5easy_bits/H5Easy_scalar.hpp
@@ -0,0 +1,134 @@
+/*
+ *  Copyright (c), 2017, Adrien Devresse <adrien.devresse@epfl.ch>
+ *
+ *  Distributed under the Boost Software License, Version 1.0.
+ *    (See accompanying file LICENSE_1_0.txt or copy at
+ *          http://www.boost.org/LICENSE_1_0.txt)
+ *
+ */
+#ifndef H5EASY_BITS_SCALAR_HPP
+#define H5EASY_BITS_SCALAR_HPP
+
+#include "../H5Easy.hpp"
+#include "H5Easy_misc.hpp"
+
+namespace H5Easy {
+
+namespace detail {
+
+/*
+Base template for partial specialization: the fallback if specialized templates don't match.
+Used e.g. for scalars.
+*/
+template <typename T, typename = void>
+struct io_impl {
+
+    inline static DataSet dump(File& file,
+                               const std::string& path,
+                               const T& data,
+                               const DumpOptions& options) {
+        DataSet dataset = initScalarDataset(file, path, data, options);
+        dataset.write(data);
+        if (options.flush()) {
+            file.flush();
+        }
+        return dataset;
+    }
+
+    inline static T load(const File& file, const std::string& path) {
+        DataSet dataset = file.getDataSet(path);
+        T data;
+        dataset.read(data);
+        return data;
+    }
+
+    inline static Attribute dumpAttribute(File& file,
+                                          const std::string& path,
+                                          const std::string& key,
+                                          const T& data,
+                                          const DumpOptions& options) {
+        Attribute attribute = initScalarAttribute(file, path, key, data, options);
+        attribute.write(data);
+        if (options.flush()) {
+            file.flush();
+        }
+        return attribute;
+    }
+
+    inline static T loadAttribute(const File& file,
+                                  const std::string& path,
+                                  const std::string& key) {
+        DataSet dataset = file.getDataSet(path);
+        Attribute attribute = dataset.getAttribute(key);
+        T data;
+        attribute.read(data);
+        return data;
+    }
+
+    inline static DataSet dump_extend(File& file,
+                                      const std::string& path,
+                                      const T& data,
+                                      const std::vector<size_t>& idx,
+                                      const DumpOptions& options) {
+        std::vector<size_t> ones(idx.size(), 1);
+
+        if (file.exist(path)) {
+            DataSet dataset = file.getDataSet(path);
+            std::vector<size_t> dims = dataset.getDimensions();
+            std::vector<size_t> shape = dims;
+            if (dims.size() != idx.size()) {
+                throw detail::error(file, path,
+                    "H5Easy::dump: Dimension of the index and the existing field do not match");
+            }
+            for (size_t i = 0; i < dims.size(); ++i) {
+                shape[i] = std::max(dims[i], idx[i] + 1);
+            }
+            if (shape != dims) {
+                dataset.resize(shape);
+            }
+            dataset.select(idx, ones).write(data);
+            if (options.flush()) {
+                file.flush();
+            }
+            return dataset;
+        }
+
+        std::vector<size_t> shape = idx;
+        const size_t unlim = DataSpace::UNLIMITED;
+        std::vector<size_t> unlim_shape(idx.size(), unlim);
+        std::vector<hsize_t> chunks(idx.size(), 10);
+        if (options.isChunked()) {
+            chunks = options.getChunkSize();
+            if (chunks.size() != idx.size()) {
+                throw error(file, path, "H5Easy::dump: Incorrect dimension ChunkSize");
+            }
+        }
+        for (size_t& i : shape) {
+            i++;
+        }
+        DataSpace dataspace = DataSpace(shape, unlim_shape);
+        DataSetCreateProps props;
+        props.add(Chunking(chunks));
+        DataSet dataset = file.createDataSet(path, dataspace, AtomicType<T>(), props, {}, true);
+        dataset.select(idx, ones).write(data);
+        if (options.flush()) {
+            file.flush();
+        }
+        return dataset;
+    }
+
+    inline static T load_part(const File& file,
+                              const std::string& path,
+                              const std::vector<size_t>& idx) {
+        std::vector<size_t> ones(idx.size(), 1);
+        DataSet dataset = file.getDataSet(path);
+        T data;
+        dataset.select(idx, ones).read(data);
+        return data;
+    }
+};
+
+}  // namespace detail
+}  // namespace H5Easy
+
+#endif  // H5EASY_BITS_SCALAR_HPP
diff --git a/vendor/highfive/include/highfive/h5easy_bits/H5Easy_vector.hpp b/vendor/highfive/include/highfive/h5easy_bits/H5Easy_vector.hpp
new file mode 100644
index 00000000..8033f901
--- /dev/null
+++ b/vendor/highfive/include/highfive/h5easy_bits/H5Easy_vector.hpp
@@ -0,0 +1,81 @@
+/*
+ *  Copyright (c), 2017, Adrien Devresse <adrien.devresse@epfl.ch>
+ *
+ *  Distributed under the Boost Software License, Version 1.0.
+ *    (See accompanying file LICENSE_1_0.txt or copy at
+ *          http://www.boost.org/LICENSE_1_0.txt)
+ *
+ */
+#ifndef H5EASY_BITS_VECTOR_HPP
+#define H5EASY_BITS_VECTOR_HPP
+
+#include "../H5Easy.hpp"
+#include "H5Easy_misc.hpp"
+#include "H5Easy_scalar.hpp"
+
+namespace H5Easy {
+
+namespace detail {
+
+template <class T>
+struct is_vector : std::false_type {};
+template <class T>
+struct is_vector<std::vector<T>> : std::true_type {};
+
+using HighFive::details::inspector;
+
+template <typename T>
+struct io_impl<T, typename std::enable_if<is_vector<T>::value>::type> {
+
+    inline static DataSet dump(File& file,
+                               const std::string& path,
+                               const T& data,
+                               const DumpOptions& options) {
+        using value_type = typename inspector<T>::base_type;
+        auto dims = inspector<T>::getDimensions(data);
+        DataSet dataset = initDataset<value_type>(file, path, std::vector<size_t>(dims.begin(), dims.end()), options);
+        dataset.write(data);
+        if (options.flush()) {
+            file.flush();
+        }
+        return dataset;
+    }
+
+    inline static T load(const File& file, const std::string& path) {
+        DataSet dataset = file.getDataSet(path);
+        T data;
+        dataset.read(data);
+        return data;
+    }
+
+   inline static Attribute dumpAttribute(File& file,
+                                         const std::string& path,
+                                         const std::string& key,
+                                         const T& data,
+                                         const DumpOptions& options) {
+        using value_type = typename inspector<T>::base_type;
+        auto dims = inspector<T>::getDimensions(data);
+        std::vector<size_t> shape(dims.begin(), dims.end());
+        Attribute attribute = initAttribute<value_type>(file, path, key, shape, options);
+        attribute.write(data);
+        if (options.flush()) {
+            file.flush();
+        }
+        return attribute;
+    }
+
+    inline static T loadAttribute(const File& file,
+                                  const std::string& path,
+                                  const std::string& key) {
+        DataSet dataset = file.getDataSet(path);
+        Attribute attribute = dataset.getAttribute(key);
+        T data;
+        attribute.read(data);
+        return data;
+    }
+};
+
+}  // namespace detail
+}  // namespace H5Easy
+
+#endif  // H5EASY_BITS_VECTOR_HPP
diff --git a/vendor/highfive/include/highfive/h5easy_bits/H5Easy_xtensor.hpp b/vendor/highfive/include/highfive/h5easy_bits/H5Easy_xtensor.hpp
new file mode 100644
index 00000000..e4ad4f5f
--- /dev/null
+++ b/vendor/highfive/include/highfive/h5easy_bits/H5Easy_xtensor.hpp
@@ -0,0 +1,85 @@
+/*
+ *  Copyright (c), 2017, Adrien Devresse <adrien.devresse@epfl.ch>
+ *
+ *  Distributed under the Boost Software License, Version 1.0.
+ *    (See accompanying file LICENSE_1_0.txt or copy at
+ *          http://www.boost.org/LICENSE_1_0.txt)
+ *
+ */
+#ifndef H5EASY_BITS_XTENSOR_HPP
+#define H5EASY_BITS_XTENSOR_HPP
+
+#include "../H5Easy.hpp"
+#include "H5Easy_misc.hpp"
+#include "H5Easy_scalar.hpp"
+
+#ifdef H5_USE_XTENSOR
+
+namespace H5Easy {
+
+namespace detail {
+
+template <typename T>
+struct io_impl<T, typename std::enable_if<xt::is_xexpression<T>::value>::type> {
+
+    inline static std::vector<size_t> shape(const T& data) {
+        return std::vector<size_t>(data.shape().cbegin(), data.shape().cend());
+    }
+
+    inline static DataSet dump(File& file,
+                               const std::string& path,
+                               const T& data,
+                               const DumpOptions& options) {
+        using value_type = typename std::decay_t<T>::value_type;
+        DataSet dataset = initDataset<value_type>(file, path, shape(data), options);
+        dataset.write_raw(data.data());
+        if (options.flush()) {
+            file.flush();
+        }
+        return dataset;
+    }
+
+    inline static T load(const File& file, const std::string& path) {
+        static_assert(xt::has_data_interface<T>::value,
+            "Cannot load to xt::xfunction or xt::xgenerator, use e.g. xt::xtensor or xt::xarray");
+        DataSet dataset = file.getDataSet(path);
+        std::vector<size_t> dims = dataset.getDimensions();
+        T data = T::from_shape(dims);
+        dataset.read(data.data());
+        return data;
+    }
+
+    inline static Attribute dumpAttribute(File& file,
+                                          const std::string& path,
+                                          const std::string& key,
+                                          const T& data,
+                                          const DumpOptions& options) {
+        using value_type = typename std::decay_t<T>::value_type;
+        Attribute attribute = initAttribute<value_type>(file, path, key, shape(data), options);
+        attribute.write_raw(data.data());
+        if (options.flush()) {
+            file.flush();
+        }
+        return attribute;
+    }
+
+    inline static T loadAttribute(const File& file,
+                                  const std::string& path,
+                                  const std::string& key) {
+        static_assert(xt::has_data_interface<T>::value,
+            "Cannot load to xt::xfunction or xt::xgenerator, use e.g. xt::xtensor or xt::xarray");
+        DataSet dataset = file.getDataSet(path);
+        Attribute attribute = dataset.getAttribute(key);
+        DataSpace dataspace = attribute.getSpace();
+        std::vector<size_t> dims = dataspace.getDimensions();
+        T data = T::from_shape(dims);
+        attribute.read(data.data());
+        return data;
+    }
+};
+
+}  // namespace detail
+}  // namespace H5Easy
+
+#endif  // H5_USE_XTENSOR
+#endif  // H5EASY_BITS_XTENSOR_HPP
diff --git a/vendor/highfive/src/examples/CMakeLists.txt b/vendor/highfive/src/examples/CMakeLists.txt
new file mode 100644
index 00000000..77e166bc
--- /dev/null
+++ b/vendor/highfive/src/examples/CMakeLists.txt
@@ -0,0 +1,29 @@
+include_directories(${HDF5_INCLUDE_DIRS})
+
+function(compile_example exemple_source)
+
+    get_filename_component(example_filename ${exemple_source} NAME)
+    string(REPLACE ".cpp" "_bin" example_name ${example_filename})
+
+    if(${example_filename} MATCHES ".*boost.*")
+        if(NOT HIGHFIVE_USE_BOOST)
+            return()
+        endif()
+    endif()
+
+    if(${example_filename} MATCHES ".*parallel_hdf5.*")
+        if(NOT HIGHFIVE_PARALLEL_HDF5)
+            return()
+        endif()
+    endif()
+
+    add_executable(${example_name} ${exemple_source})
+    target_link_libraries(${example_name} HighFive)
+
+endfunction()
+
+file(GLOB list_example "*.cpp")
+
+foreach(example_src ${list_example})
+    compile_example(${example_src})
+endforeach()
diff --git a/vendor/highfive/src/examples/boost_multi_array_2D.cpp b/vendor/highfive/src/examples/boost_multi_array_2D.cpp
new file mode 100644
index 00000000..e3d2b7d6
--- /dev/null
+++ b/vendor/highfive/src/examples/boost_multi_array_2D.cpp
@@ -0,0 +1,54 @@
+/*
+ *  Copyright (c), 2017, Adrien Devresse
+ *
+ *  Distributed under the Boost Software License, Version 1.0.
+ *    (See accompanying file LICENSE_1_0.txt or copy at
+ *          http://www.boost.org/LICENSE_1_0.txt)
+ *
+ */
+#include <iostream>
+
+#undef H5_USE_BOOST
+#define H5_USE_BOOST
+
+#include <boost/multi_array.hpp>
+#include <highfive/H5File.hpp>
+
+using namespace HighFive;
+
+const std::string FILE_NAME("boost_multiarray_example.h5");
+const std::string DATASET_NAME("dset");
+const int size_x = 10;
+const int size_y = 3;
+
+// Create a 2D dataset 10x3 of double with boost multi array
+// and write it to a file
+int main(void) {
+
+    try {
+
+        boost::multi_array<double, 2> my_array(boost::extents[size_x][size_y]);
+
+        for (int i = 0; i < size_x; ++i) {
+            for (int j = 0; j < size_y; ++j) {
+                my_array[i][j] = double(j + i * size_y);
+            }
+        }
+
+        // we create a new hdf5 file
+        File file(FILE_NAME, File::ReadWrite | File::Create | File::Truncate);
+
+        // let's create our dataset of the size of the boost::multi_array
+        DataSet dataset =
+            file.createDataSet<double>(DATASET_NAME, DataSpace::From(my_array));
+
+        // we fill it
+        dataset.write(my_array);
+
+    } catch (Exception& err) {
+        // catch and print any HDF5 error
+        std::cerr << err.what() << std::endl;
+    }
+
+    return 0; // successfully terminated
+}
diff --git a/vendor/highfive/src/examples/boost_multiarray_complex.cpp b/vendor/highfive/src/examples/boost_multiarray_complex.cpp
new file mode 100644
index 00000000..a5c19858
--- /dev/null
+++ b/vendor/highfive/src/examples/boost_multiarray_complex.cpp
@@ -0,0 +1,31 @@
+/*
+ *  Copyright (c), 2020 Blue Brain Project - EPFL
+ *
+ *  Distributed under the Boost Software License, Version 1.0.
+ *    (See accompanying file LICENSE_1_0.txt or copy at
+ *          http://www.boost.org/LICENSE_1_0.txt)
+ *
+ */
+#include <algorithm>
+#include <complex>
+
+#undef H5_USE_BOOST
+#define H5_USE_BOOST
+
+#include <boost/multi_array.hpp>
+#include <highfive/H5DataSet.hpp>
+#include <highfive/H5DataSpace.hpp>
+#include <highfive/H5File.hpp>
+
+typedef std::complex<double> complex_t;
+
+int main() {
+    boost::multi_array<complex_t, 4> multi_array(boost::extents[3][2][1][1]);
+    std::fill_n(multi_array.origin(), multi_array.num_elements(), 1.0);
+    multi_array[1][1][0][0] = complex_t{1.1, 1.2};
+
+    HighFive::File file("multi_array_complex.h5", HighFive::File::Truncate);
+
+    HighFive::DataSet dataset = file.createDataSet("multi_array", multi_array);
+    return 0;
+}
diff --git a/vendor/highfive/src/examples/boost_ublas_double.cpp b/vendor/highfive/src/examples/boost_ublas_double.cpp
new file mode 100644
index 00000000..98d57af1
--- /dev/null
+++ b/vendor/highfive/src/examples/boost_ublas_double.cpp
@@ -0,0 +1,65 @@
+/*
+ *  Copyright (c), 2017, Adrien Devresse
+ *
+ *  Distributed under the Boost Software License, Version 1.0.
+ *    (See accompanying file LICENSE_1_0.txt or copy at
+ *          http://www.boost.org/LICENSE_1_0.txt)
+ *
+ */
+#include <iostream>
+
+#undef H5_USE_BOOST
+#define H5_USE_BOOST
+
+// In some versions of Boost (starting with 1.64), you have to include the serialization header before ublas
+#include <boost/serialization/vector.hpp>
+
+#include <boost/numeric/ublas/io.hpp>
+#include <boost/numeric/ublas/matrix.hpp>
+#include <highfive/H5File.hpp>
+
+using namespace HighFive;
+
+const std::string FILE_NAME("boost_ublas_double.h5");
+const std::string DATASET_NAME("dset");
+
+const size_t size_x = 10;
+const size_t size_y = 10;
+
+int main(void) {
+
+    try {
+        typedef typename boost::numeric::ublas::matrix<double> Matrix;
+
+        // create a 10x10 matrix
+        Matrix mat(size_x, size_y);
+
+        // fill it
+        for (std::size_t i = 0; i < size_x; ++i) {
+            mat(i, i) = static_cast<double>(i);
+        }
+
+        // Create a new HDF5 file
+        File file(FILE_NAME, File::ReadWrite | File::Create | File::Truncate);
+
+        // create a new dataset with the 10x10 Matrix dimension
+        DataSet dataset =
+            file.createDataSet<double>(DATASET_NAME, DataSpace::From(mat));
+
+        // write it
+        dataset.write(mat);
+
+        // now, let read it back
+        Matrix result;
+        dataset.read(result);
+
+        // print what we read
+        std::cout << "Matrix result:\n" << result << std::endl;
+
+    } catch (Exception& err) {
+        // catch and print any HDF5 error
+        std::cerr << err.what() << std::endl;
+    }
+
+    return 0; // successfully terminated
+}
diff --git a/vendor/highfive/src/examples/compound_types.cpp b/vendor/highfive/src/examples/compound_types.cpp
new file mode 100644
index 00000000..2527d005
--- /dev/null
+++ b/vendor/highfive/src/examples/compound_types.cpp
@@ -0,0 +1,44 @@
+/*
+ *  Copyright (c), 2021, Blue Brain Project, EPFL
+ *
+ *  Distributed under the Boost Software License, Version 1.0.
+ *    (See accompanying file LICENSE_1_0.txt or copy at
+ *          http://www.boost.org/LICENSE_1_0.txt)
+ *
+ */
+
+// Compound datatype test :: May 2021
+// //////////////////////////////////
+
+#include <highfive/H5File.hpp>
+
+
+typedef struct {
+    double width;
+    double height;
+} Size2D;
+
+
+HighFive::CompoundType create_compound_Size2D() {
+    return {{ "width",  HighFive::AtomicType<double>{} },
+            { "height", HighFive::AtomicType<double>{} }};
+}
+
+HIGHFIVE_REGISTER_TYPE(Size2D, create_compound_Size2D)
+
+int main() {
+    const std::string FILE_NAME("compounds_test.h5");
+    const std::string DATASET_NAME("dims");
+
+    HighFive::File file(FILE_NAME, HighFive::File::Truncate);
+
+    auto t1 = create_compound_Size2D();
+    t1.commit(file, "Size2D");
+
+    std::vector<Size2D> dims = {{1., 2.5}, {3., 4.5}};
+    auto dataset = file.createDataSet(DATASET_NAME, dims);
+
+    auto g1 = file.createGroup("group1");
+    g1.createAttribute(DATASET_NAME, dims);
+
+}
\ No newline at end of file
diff --git a/vendor/highfive/src/examples/create_attribute_string_integer.cpp b/vendor/highfive/src/examples/create_attribute_string_integer.cpp
new file mode 100644
index 00000000..8bbfa1fb
--- /dev/null
+++ b/vendor/highfive/src/examples/create_attribute_string_integer.cpp
@@ -0,0 +1,72 @@
+/*
+ *  Copyright (c), 2017, Adrien Devresse
+ *
+ *  Distributed under the Boost Software License, Version 1.0.
+ *    (See accompanying file LICENSE_1_0.txt or copy at
+ *          http://www.boost.org/LICENSE_1_0.txt)
+ *
+ */
+#include <iostream>
+#include <string>
+#include <vector>
+
+#include <highfive/H5Attribute.hpp>
+#include <highfive/H5File.hpp>
+#include <highfive/H5DataSet.hpp>
+#include <highfive/H5DataSpace.hpp>
+
+using namespace HighFive;
+
+const std::string FILE_NAME("create_attribute.h5");
+const std::string DATASET_NAME("my_dataset");
+
+const std::string ATTRIBUTE_NAME_NOTE("note");
+const std::string ATTRIBUTE_NAME_VERSION("version_string");
+
+// create a dataset from a vector of string
+// read it back and print it
+int main(void) {
+
+    try {
+        // Create a new file using the default property lists.
+        File file(FILE_NAME, File::ReadWrite | File::Create | File::Truncate);
+
+        // Create a dummy dataset of one single integer
+        DataSet dataset =
+            file.createDataSet(DATASET_NAME, DataSpace(1), AtomicType<int>());
+
+        // Now let's add a attribute on this dataset
+        // This attribute will be named "note"
+        // and have the following content
+        std::string string_list("very important Dataset !");
+
+        Attribute a = dataset.createAttribute<std::string>(
+            ATTRIBUTE_NAME_NOTE, DataSpace::From(string_list));
+        a.write(string_list);
+
+        // We also add a "version" attribute
+        // that will be an array 1x2 of integer
+        std::vector<int> version;
+        version.push_back(1);
+        version.push_back(0); // version 1.0
+
+        Attribute v = dataset.createAttribute<int>(ATTRIBUTE_NAME_VERSION,
+                                                   DataSpace::From(version));
+        v.write(version);
+
+        // Ok all attributes are now written
+
+        // let's list the keys of all attributes now
+        std::vector<std::string> all_attributes_keys =
+            dataset.listAttributeNames();
+        for (const auto& attr : all_attributes_keys) {
+            std::cout << "attribute: " << attr << std::endl;
+        }
+
+    } catch (Exception& err) {
+        // catch and print any HDF5 error
+        std::cerr << err.what() << std::endl;
+    }
+
+    return 0; // successfully terminated
+}
diff --git a/vendor/highfive/src/examples/create_dataset_double.cpp b/vendor/highfive/src/examples/create_dataset_double.cpp
new file mode 100644
index 00000000..5daa9825
--- /dev/null
+++ b/vendor/highfive/src/examples/create_dataset_double.cpp
@@ -0,0 +1,47 @@
+/*
+ *  Copyright (c), 2017, Adrien Devresse
+ *
+ *  Distributed under the Boost Software License, Version 1.0.
+ *    (See accompanying file LICENSE_1_0.txt or copy at
+ *          http://www.boost.org/LICENSE_1_0.txt)
+ *
+ */
+#include <iostream>
+#include <string>
+#include <vector>
+
+#include <highfive/H5DataSet.hpp>
+#include <highfive/H5DataSpace.hpp>
+#include <highfive/H5File.hpp>
+
+const std::string FILE_NAME("create_dataset_example.h5");
+const std::string DATASET_NAME("dset");
+
+// Create a dataset name "dset" of double 4x6
+//
+int main(void) {
+    using namespace HighFive;
+    try {
+        // Create a new file using the default property lists.
+        File file(FILE_NAME, File::ReadWrite | File::Create | File::Truncate);
+
+        // Define the size of our dataset: 2x6
+        std::vector<size_t> dims{2, 6};
+
+        // Create the dataset
+        DataSet dataset =
+            file.createDataSet<double>(DATASET_NAME, DataSpace(dims));
+
+        double data[2][6] = {{1.1, 2.2, 3.3, 4.4, 5.5, 6.6},
+                             {11.11, 12.12, 13.13, 14.14, 15.15, 16.16}};
+
+        // write it
+        dataset.write(data);
+
+    } catch (Exception& err) {
+        // catch and print any HDF5 error
+        std::cerr << err.what() << std::endl;
+    }
+
+    return 0; // successfully terminated
+}
diff --git a/vendor/highfive/src/examples/create_datatype.cpp b/vendor/highfive/src/examples/create_datatype.cpp
new file mode 100644
index 00000000..e1965a93
--- /dev/null
+++ b/vendor/highfive/src/examples/create_datatype.cpp
@@ -0,0 +1,118 @@
+#include <iostream>
+
+#include <highfive/H5File.hpp>
+#include <highfive/H5DataType.hpp>
+
+using namespace HighFive;
+
+static const std::string FILE_NAME("create_datatype_example.h5");
+static const std::string DATASET_NAME("test_dataset");
+
+
+// Struct representation of custom type (type v below)
+typedef struct {
+    char a;
+    short b;
+    unsigned long long c;
+} csl;
+
+bool operator==(csl x, csl y) {
+    return x.a == y.a && x.b == y.b && x.c == y.c;
+}
+
+bool operator!=(csl x, csl y) {
+    return !(x == y);
+}
+
+// Tell HighFive how to create the HDF5 datatype for this base type by
+// using the HIGHFIVE_REGISTER_TYPE macro
+CompoundType create_compound_csl() {
+    return {{"u1", AtomicType<unsigned char>{}},
+            {"u2", AtomicType<short>{}},
+            {"u3", AtomicType<unsigned long long>{}}};
+}
+HIGHFIVE_REGISTER_TYPE(csl, create_compound_csl)
+
+int main(void) {
+
+    try {
+
+        File file(FILE_NAME, File::ReadWrite | File::Create | File::Truncate);
+
+        // Create a simple compound type with automatic alignment of the
+        // members. For this the type alignment is trivial.
+        std::vector<CompoundType::member_def> t_members({
+            {"real", AtomicType<int>{}},
+            {"imag", AtomicType<int>{}}
+        });
+        CompoundType t(t_members);
+        t.commit(file, "new_type1");
+
+        // Create a complex nested datatype with manual alignment
+        CompoundType u({{"u1", t, 0},
+                        {"u2", t, 9},
+                        {"u3", AtomicType<int>{}, 20}},
+                       26);
+        u.commit(file, "new_type3");
+
+        // Create a more complex type with automatic alignment. For this the
+        // type alignment is more complex.
+        CompoundType v_aligned{{"u1", AtomicType<unsigned char>{}},
+                               {"u2", AtomicType<short>{}},
+                               {"u3", AtomicType<unsigned long long>{}}};
+        // introspect the compound type
+        std::cout << "v_aligned size: " << v_aligned.getSize();
+        for (const auto& member : v_aligned.getMembers()) {
+            std::cout << "  field " << member.name << " offset: " << member.offset
+                      << std::endl;
+        }
+
+        v_aligned.commit(file, "new_type2_aligned");
+
+        // Create a more complex type with a fully packed alignment. The
+        // equivalent type is created with a standard struct alignment in the
+        // implementation of HighFive::create_datatype above
+        CompoundType v_packed({{"u1", AtomicType<unsigned char>{}, 0},
+                               {"u2", AtomicType<short>{}, 1},
+                               {"u3", AtomicType<unsigned long long>{}, 3}},
+                              11);
+        v_packed.commit(file, "new_type2_packed");
+
+
+        // Initialise some data
+        std::vector<csl> data;
+        data.push_back({'f', 1, 4});
+        data.push_back({'g', -4, 18});
+
+        // Write the data into the file in a fully packed form
+        DataSet dataset = file.createDataSet(DATASET_NAME, DataSpace(2), v_packed);
+        dataset.write(data);
+
+        file.flush();
+
+        // Read a subset of the data back
+        std::vector<csl> result;
+        dataset.select({0}, {2}).read(result);
+
+        for(size_t i = 0; i < data.size(); ++i) {
+            if (result[i] != data[i]) {
+                std::cout << "result[" << i << "]:" << std::endl;
+                std::cout << "    " << result[i].a << std::endl;
+                std::cout << "    " << result[i].b << std::endl;
+                std::cout << "    " << result[i].c << std::endl;
+                std::cout << "data[" << i << "]:" << std::endl;
+                std::cout << "    " << data[i].a << std::endl;
+                std::cout << "    " << data[i].b << std::endl;
+                std::cout << "    " << data[i].c << std::endl;
+            }
+        }
+
+
+    } catch (const Exception& err) {
+        // catch and print any HDF5 error
+        std::cerr << err.what() << std::endl;
+        return 1;
+    }
+
+    return 0; // successfully terminated
+}
diff --git a/vendor/highfive/src/examples/create_extensible_dataset.cpp b/vendor/highfive/src/examples/create_extensible_dataset.cpp
new file mode 100644
index 00000000..773ceaa3
--- /dev/null
+++ b/vendor/highfive/src/examples/create_extensible_dataset.cpp
@@ -0,0 +1,71 @@
+/*
+ *  Copyright (c), 2017, Adrien Devresse
+ *
+ *  Distributed under the Boost Software License, Version 1.0.
+ *    (See accompanying file LICENSE_1_0.txt or copy at
+ *          http://www.boost.org/LICENSE_1_0.txt)
+ *
+ */
+#include <iostream>
+#include <string>
+#include <vector>
+
+#include <highfive/H5DataSet.hpp>
+#include <highfive/H5DataSpace.hpp>
+#include <highfive/H5File.hpp>
+
+const std::string FILE_NAME("create_extensible_dataset_example.h5");
+const std::string DATASET_NAME("dset");
+
+// Create a dataset name "dset" of double 4x6
+//
+int main(void) {
+    using namespace HighFive;
+    try {
+        // Create a new file using the default property lists.
+        File file(FILE_NAME, File::ReadWrite | File::Create | File::Truncate);
+
+        // Create a dataspace with initial shape and max shape
+        DataSpace dataspace = DataSpace({4, 5}, {17, DataSpace::UNLIMITED});
+
+        // Use chunking
+        DataSetCreateProps props;
+        props.add(Chunking(std::vector<hsize_t>{2, 2}));
+
+        // Create the dataset
+        DataSet dataset = file.createDataSet(DATASET_NAME, dataspace,
+                                             AtomicType<double>(), props);
+
+        // Write into the initial part of the dataset
+        double t1[3][1] = {{2.0}, {2.0}, {4.0}};
+        dataset.select({0, 0}, {3, 1}).write(t1);
+
+        // Resize the dataset to a larger size
+        dataset.resize({4, 6});
+
+        // Write into the new part of the dataset
+        double t2[1][3] = {{4.0, 8.0, 6.0}};
+        dataset.select({3, 3}, {1, 3}).write(t2);
+
+        // now we read it back
+        std::vector<std::vector<double>> result;
+        dataset.read(result);
+
+        // we print it out and see:
+        // 2 0 0 0 0 0
+        // 2 0 0 0 0 0
+        // 4 0 0 0 0 0
+        // 0 0 0 4 8 6
+        for (auto row : result) {
+            for (auto col : row)
+                std::cout << " " << col;
+            std::cout << std::endl;
+        }
+
+    } catch (const Exception& err) {
+        // catch and print any HDF5 error
+        std::cerr << err.what() << std::endl;
+    }
+
+    return 0; // successfully terminated
+}
diff --git a/vendor/highfive/src/examples/easy_attribute.cpp b/vendor/highfive/src/examples/easy_attribute.cpp
new file mode 100644
index 00000000..097df060
--- /dev/null
+++ b/vendor/highfive/src/examples/easy_attribute.cpp
@@ -0,0 +1,40 @@
+/// To enable plug-ins, load the relevant libraries BEFORE HighFive. E.g.
+///
+///   #include <xtensor/xtensor.hpp>
+///   #include <Eigen/Eigen>
+///   #include <highfive/H5Easy.hpp>
+///
+/// or ask HighFive to include them. E.g.
+///
+///   #define H5_USE_XTENSOR
+///   #define H5_USE_EIGEN
+///   #include <highfive/H5Easy.hpp>
+///
+
+// optionally enable plug-in xtensor
+#ifdef H5_USE_XTENSOR
+#include <xtensor/xtensor.hpp>
+#endif
+
+// optionally enable plug-in Eigen
+#ifdef H5_USE_EIGEN
+#include <Eigen/Eigen>
+#endif
+
+#include <highfive/H5Easy.hpp>
+
+int main()
+{
+    H5Easy::File file("example.h5", H5Easy::File::Overwrite);
+
+    std::vector<double> measurement = {1.0, 2.0, 3.0};
+    std::string desc = "This is an important dataset.";
+    double temperature = 1.234;
+
+    H5Easy::dump(file, "/path/to/measurement", measurement);
+    H5Easy::dumpAttribute(file, "/path/to/measurement", "description", desc);
+    H5Easy::dumpAttribute(file, "/path/to/measurement", "temperature", temperature);
+
+
+    return 0;
+}
diff --git a/vendor/highfive/src/examples/easy_dumpoptions.cpp b/vendor/highfive/src/examples/easy_dumpoptions.cpp
new file mode 100644
index 00000000..8d2d162d
--- /dev/null
+++ b/vendor/highfive/src/examples/easy_dumpoptions.cpp
@@ -0,0 +1,90 @@
+/// To enable plug-ins, load the relevant libraries BEFORE HighFive. E.g.
+///
+///   #include <xtensor/xtensor.hpp>
+///   #include <Eigen/Eigen>
+///   #include <highfive/H5Easy.hpp>
+///
+/// or ask HighFive to include them. E.g.
+///
+///   #define H5_USE_XTENSOR
+///   #define H5_USE_EIGEN
+///   #include <highfive/H5Easy.hpp>
+///
+
+// optionally enable plug-in xtensor
+#ifdef H5_USE_XTENSOR
+#include <xtensor/xtensor.hpp>
+#endif
+
+// optionally enable plug-in Eigen
+#ifdef H5_USE_EIGEN
+#include <Eigen/Eigen>
+#endif
+
+#include <highfive/H5Easy.hpp>
+
+int main()
+{
+    H5Easy::File file("example.h5", H5Easy::File::Overwrite);
+
+    // plain options
+    {
+        std::vector<double> A = {1.0, 2.0, 3.0};
+
+        H5Easy::dump(file, "/path/to/A", A);
+        H5Easy::dump(file, "/path/to/A", A, H5Easy::DumpMode::Overwrite);
+    }
+
+    // advanced - compression
+    {
+        std::vector<double> B = {1.0, 2.0, 3.0};
+
+        H5Easy::dump(file, "/path/to/B", B,
+            H5Easy::DumpOptions(H5Easy::Compression()));
+
+        H5Easy::dump(file, "/path/to/B", B,
+            H5Easy::DumpOptions(H5Easy::Compression(), H5Easy::DumpMode::Overwrite));
+    }
+
+    // advanced - compression - set compression level
+    {
+        std::vector<double> C = {1.0, 2.0, 3.0};
+
+        H5Easy::dump(file, "/path/to/C", C,
+            H5Easy::DumpOptions(H5Easy::Compression(8)));
+    }
+
+    // advanced - compression - set compression level & chunk size
+    {
+        std::vector<double> D = {1.0, 2.0, 3.0};
+
+        H5Easy::DumpOptions options(H5Easy::Compression(8));
+        options.setChunkSize({3});
+
+        H5Easy::dump(file, "/path/to/D", D, options);
+    }
+
+    // advanced - set chunk size
+    {
+        int E = 10;
+
+        H5Easy::DumpOptions options;
+        options.setChunkSize({100, 100});
+
+        H5Easy::dump(file, "/path/to/E", E, {0, 0}, options);
+        H5Easy::dump(file, "/path/to/E", E, {0, 1}, options);
+        // ...
+    }
+
+    // advanced - no automatic flushing
+    {
+        std::vector<double> F = {1.0, 2.0, 3.0};
+
+        H5Easy::dump(file, "/path/to/F", F,
+            H5Easy::DumpOptions(H5Easy::Flush::False));
+
+        file.flush();
+    }
+
+    return 0;
+}
diff --git a/vendor/highfive/src/examples/easy_load_dump.cpp b/vendor/highfive/src/examples/easy_load_dump.cpp
new file mode 100644
index 00000000..a256228f
--- /dev/null
+++ b/vendor/highfive/src/examples/easy_load_dump.cpp
@@ -0,0 +1,103 @@
+/// To enable plug-ins, load the relevant libraries BEFORE HighFive. E.g.
+///
+///   #include <xtensor/xtensor.hpp>
+///   #include <Eigen/Eigen>
+///   #include <highfive/H5Easy.hpp>
+///
+/// or ask HighFive to include them. E.g.
+///
+///   #define H5_USE_XTENSOR
+///   #define H5_USE_EIGEN
+///   #include <highfive/H5Easy.hpp>
+///
+
+// optionally enable plug-in xtensor
+#ifdef H5_USE_XTENSOR
+#include <xtensor/xtensor.hpp>
+#endif
+
+// optionally enable plug-in Eigen
+#ifdef H5_USE_EIGEN
+#include <Eigen/Eigen>
+#endif
+
+#include <highfive/H5Easy.hpp>
+
+int main()
+{
+    H5Easy::File file("example.h5", H5Easy::File::Overwrite);
+
+    // (over)write and read scalar
+    {
+        int A = 10;
+
+        H5Easy::dump(file, "/path/to/A", A);
+        H5Easy::dump(file, "/path/to/A", A, H5Easy::DumpMode::Overwrite);
+    }
+
+    // (over)write and read std::vector
+    {
+        std::vector<double> B = {1., 2., 3.};
+
+        H5Easy::dump(file, "/path/to/B", B);
+        H5Easy::dump(file, "/path/to/B", B, H5Easy::DumpMode::Overwrite);
+
+        B = H5Easy::load<std::vector<double>>(file, "/path/to/B");
+    }
+
+    // (over)write scalar in (automatically expanding) extendible DataSet,
+    // read item from the DataSet
+    {
+        int C = 10;
+
+        H5Easy::dump(file, "/path/to/C", C, {0});
+        H5Easy::dump(file, "/path/to/C", C, {1});
+        H5Easy::dump(file, "/path/to/C", C, {3});
+
+        C = H5Easy::load<int>(file, "/path/to/C", {0});
+    }
+
+    // get the size/shape of a DataSet
+    {
+        // outputs "size_t"
+        H5Easy::getSize(file, "/path/to/C");
+
+        // outputs "std::vector<size_t>"
+        H5Easy::getShape(file, "/path/to/C");
+    }
+
+#ifdef H5_USE_EIGEN
+    // (over)write and read Eigen::Matrix
+    {
+        // matrix
+        Eigen::MatrixXd D = Eigen::MatrixXd::Random(10,5);
+
+        H5Easy::dump(file, "/path/to/D", D);
+        H5Easy::dump(file, "/path/to/D", D, H5Easy::DumpMode::Overwrite);
+
+        D = H5Easy::load<Eigen::MatrixXd>(file, "/path/to/D");
+
+
+        Eigen::ArrayXd D2 = Eigen::ArrayXd::Random(30);
+
+        H5Easy::dump(file, "/path/to/D2", D2);
+        H5Easy::dump(file, "/path/to/D2", D2, H5Easy::DumpMode::Overwrite);
+
+        D2 = H5Easy::load<Eigen::ArrayXd>(file, "/path/to/D2");
+    }
+#endif
+
+#ifdef H5_USE_XTENSOR
+    // (over)write and read xt::xtensor (or xt::xarray)
+    {
+        xt::xtensor<size_t,1> E = xt::arange<size_t>(10);
+
+        H5Easy::dump(file, "/path/to/E", E);
+        H5Easy::dump(file, "/path/to/E", E, H5Easy::DumpMode::Overwrite);
+
+        E = H5Easy::load<xt::xtensor<size_t,1>>(file, "/path/to/E");
+    }
+#endif
+
+    return 0;
+}
diff --git a/vendor/highfive/src/examples/parallel_hdf5_write_dataset.cpp b/vendor/highfive/src/examples/parallel_hdf5_write_dataset.cpp
new file mode 100644
index 00000000..504e33d1
--- /dev/null
+++ b/vendor/highfive/src/examples/parallel_hdf5_write_dataset.cpp
@@ -0,0 +1,69 @@
+/*
+ *  Copyright (c), 2017, Adrien Devresse
+ *
+ *  Distributed under the Boost Software License, Version 1.0.
+ *    (See accompanying file LICENSE_1_0.txt or copy at
+ *          http://www.boost.org/LICENSE_1_0.txt)
+ *
+ */
+#include <iostream>
+#include <string>
+#include <vector>
+
+#include <mpi.h>
+
+#include <highfive/H5DataSet.hpp>
+#include <highfive/H5DataSpace.hpp>
+#include <highfive/H5File.hpp>
+
+const std::string FILE_NAME("parallel_dataset_example.h5");
+const std::string DATASET_NAME("dset");
+
+//
+// simple example to write a dataset with Parallel HDF5 with MPI-IO
+//
+// The dataset is written from several MPI node in parallel
+//
+//
+int main(int argc, char** argv) {
+    int mpi_rank, mpi_size;
+
+    // initialize MPI
+    MPI_Init(&argc, &argv);
+    MPI_Comm_size(MPI_COMM_WORLD, &mpi_size);
+    MPI_Comm_rank(MPI_COMM_WORLD, &mpi_rank);
+
+    using namespace HighFive;
+    try {
+
+        // open a new file with the MPI IO driver for parallel Read/Write
+        File file(FILE_NAME, File::ReadWrite | File::Create | File::Truncate,
+                  MPIOFileDriver(MPI_COMM_WORLD, MPI_INFO_NULL));
+
+        // we define the size of our dataset to
+        //  lines : total number of mpi_rank
+        //  columns : 2
+        std::vector<size_t> dims(2);
+        dims[0] = std::size_t(mpi_size);
+        dims[1] = 2;
+
+        // Create the dataset
+        DataSet dataset =
+            file.createDataSet<double>(DATASET_NAME, DataSpace(dims));
+
+        // Each node want to write its own rank two time in
+        // its associated row
+        int data[1][2] = {{mpi_rank, mpi_rank}};
+
+        // write it to the associated mpi_rank
+        dataset.select({std::size_t(mpi_rank), 0}, {1, 2}).write(data);
+
+    } catch (Exception& err) {
+        // catch and print any HDF5 error
+        std::cerr << err.what() << std::endl;
+        MPI_Abort(MPI_COMM_WORLD, 1);
+    }
+
+    MPI_Finalize();
+    return 0; // successfully terminated
+}
diff --git a/vendor/highfive/src/examples/read_write_dataset_string.cpp b/vendor/highfive/src/examples/read_write_dataset_string.cpp
new file mode 100644
index 00000000..3dbfa94b
--- /dev/null
+++ b/vendor/highfive/src/examples/read_write_dataset_string.cpp
@@ -0,0 +1,61 @@
+/*
+ *  Copyright (c), 2017, Adrien Devresse
+ *
+ *  Distributed under the Boost Software License, Version 1.0.
+ *    (See accompanying file LICENSE_1_0.txt or copy at
+ *          http://www.boost.org/LICENSE_1_0.txt)
+ *
+ */
+#include <iostream>
+#include <string>
+#include <vector>
+
+#include <highfive/H5File.hpp>
+#include <highfive/H5DataSet.hpp>
+#include <highfive/H5DataSpace.hpp>
+
+using namespace HighFive;
+
+const std::string FILE_NAME("create_dataset_string_example.h5");
+const std::string DATASET_NAME("story");
+
+// create a dataset from a vector of string
+// read it back and print it
+int main(void) {
+
+    try {
+        // Create a new file using the default property lists.
+        File file(FILE_NAME, File::ReadWrite | File::Create | File::Truncate);
+
+        std::vector<std::string> string_list;
+        string_list.push_back("Hello World !");
+        string_list.push_back("This string list is mapped to a dataset of "
+                              "variable length string");
+        string_list.push_back(
+            "Encoding is done in UTF-8 - 你好 - Здравствуйте!");
+        string_list.push_back("May the force be with you");
+        string_list.push_back("Enjoy !");
+
+        // create a dataset ready to contains strings of the size of the vector
+        // string_list
+        DataSet dataset = file.createDataSet<std::string>(
+            DATASET_NAME, DataSpace::From(string_list));
+
+        // let's write our vector of  string
+        dataset.write(string_list);
+
+        // now we read it back
+        std::vector<std::string> result_string_list;
+        dataset.read(result_string_list);
+
+        for (size_t i = 0; i < result_string_list.size(); ++i) {
+            std::cout << ":" << i << " " << result_string_list[i] << "\n";
+        }
+
+    } catch (Exception& err) {
+        // catch and print any HDF5 error
+        std::cerr << err.what() << std::endl;
+    }
+
+    return 0; // successfully terminated
+}
diff --git a/vendor/highfive/src/examples/read_write_fixedlen_string.cpp b/vendor/highfive/src/examples/read_write_fixedlen_string.cpp
new file mode 100644
index 00000000..af7050b0
--- /dev/null
+++ b/vendor/highfive/src/examples/read_write_fixedlen_string.cpp
@@ -0,0 +1,53 @@
+/*
+ *  Copyright (c), 2020, Blue Brain Project
+ *
+ *  Distributed under the Boost Software License, Version 1.0.
+ *    (See accompanying file LICENSE_1_0.txt or copy at
+ *          http://www.boost.org/LICENSE_1_0.txt)
+ *
+ */
+#include <iostream>
+#include <string>
+#include <vector>
+
+#include <highfive/H5File.hpp>
+#include <highfive/H5DataSet.hpp>
+#include <highfive/H5DataSpace.hpp>
+
+using namespace HighFive;
+
+static const std::string FILE_NAME("create_dataset_string_example.h5");
+
+// create a dataset from a vector of string
+// read it back and print it
+int main(void) {
+
+    try {
+        // Create a new file using the default property lists.
+        File file(FILE_NAME, File::ReadWrite | File::Create | File::Truncate);
+        const char strings_fixed[][16] = {"abcabcabcabcabc", "123123123123123"};
+
+        // create a dataset ready to contains strings of the size of the vector
+        file.createDataSet<char[10]>("ds1", DataSpace(2)).write(strings_fixed);
+
+        // Without specific type info this will create an int8 dataset
+        file.createDataSet("ds2", strings_fixed);
+
+        // Now test the new interface type
+        FixedLenStringArray<10> arr{"0000000", "1111111"};  // also accepts std::vector
+        auto ds = file.createDataSet("ds3", arr);
+
+        // Read back truncating to 4 chars
+        FixedLenStringArray<4> array_back;
+        ds.read(array_back);
+        std::cout << "First item is '" << array_back[0] << "'" << std::endl
+                  << "Second item is '" << array_back[1] << "'" << std::endl;
+
+
+    } catch (const Exception& err) {
+        // catch and print any HDF5 error
+        std::cerr << err.what() << std::endl;
+    }
+
+    return 0; // successfully terminated
+}
diff --git a/vendor/highfive/src/examples/read_write_single_scalar.cpp b/vendor/highfive/src/examples/read_write_single_scalar.cpp
new file mode 100644
index 00000000..268fbdbf
--- /dev/null
+++ b/vendor/highfive/src/examples/read_write_single_scalar.cpp
@@ -0,0 +1,55 @@
+/*
+ *  Copyright (c), 2017, Adrien Devresse
+ *
+ *  Distributed under the Boost Software License, Version 1.0.
+ *    (See accompanying file LICENSE_1_0.txt or copy at
+ *          http://www.boost.org/LICENSE_1_0.txt)
+ *
+ */
+#include <iostream>
+#include <string>
+#include <vector>
+
+#include <highfive/H5File.hpp>
+#include <highfive/H5DataSet.hpp>
+#include <highfive/H5DataSpace.hpp>
+
+const std::string FILE_NAME("read_write_scalar.h5");
+const std::string DATASET_NAME("single_scalar");
+
+// Create a dataset name "single_scalar"
+// which contains only the perfect integer number "42"
+//
+int main(void) {
+    using namespace HighFive;
+    try {
+        // Create a new file using the default property lists.
+        File file(FILE_NAME, File::ReadWrite | File::Create | File::Truncate);
+
+        int perfect_number = 42;
+
+        // Create the dataset
+        DataSet dataset = file.createDataSet<double>(
+            DATASET_NAME, DataSpace::From(perfect_number));
+
+        // write it
+        dataset.write(perfect_number);
+
+        // flush everything
+        file.flush();
+
+        // let's read it back
+        int potentially_perfect_number;
+
+        dataset.read(potentially_perfect_number);
+
+        std::cout << "perfect number: " << potentially_perfect_number
+                  << std::endl;
+
+    } catch (Exception& err) {
+        // catch and print any HDF5 error
+        std::cerr << err.what() << std::endl;
+    }
+
+    return 0; // successfully terminated
+}
diff --git a/vendor/highfive/src/examples/read_write_vector_dataset.cpp b/vendor/highfive/src/examples/read_write_vector_dataset.cpp
new file mode 100644
index 00000000..9b9633f4
--- /dev/null
+++ b/vendor/highfive/src/examples/read_write_vector_dataset.cpp
@@ -0,0 +1,74 @@
+/*
+ *  Copyright (c), 2017, Adrien Devresse
+ *
+ *  Distributed under the Boost Software License, Version 1.0.
+ *    (See accompanying file LICENSE_1_0.txt or copy at
+ *          http://www.boost.org/LICENSE_1_0.txt)
+ *
+ */
+#include <iostream>
+#include <string>
+#include <vector>
+
+#include <highfive/H5File.hpp>
+#include <highfive/H5DataSet.hpp>
+#include <highfive/H5DataSpace.hpp>
+
+using namespace HighFive;
+
+const std::string FILE_NAME("dataset_integer.h5");
+const std::string DATASET_NAME("dset");
+const size_t size_dataset = 20;
+
+// create a dataset 1D from a vector of string
+void write_dataset() {
+
+    // we create a new hdf5 file
+    File file(FILE_NAME, File::ReadWrite | File::Create | File::Truncate);
+
+    std::vector<int> data(size_dataset);
+    for (size_t i = 0; i < data.size(); ++i) {
+        data[i] = int(i);
+    }
+
+    // let's create a dataset of native integer with the size of the vector
+    // 'data'
+    DataSet dataset =
+        file.createDataSet<int>(DATASET_NAME, DataSpace::From(data));
+
+    // let's write our vector of int to the HDF5 dataset
+    dataset.write(data);
+}
+
+// read our data back
+void read_dataset() {
+    // we open the existing hdf5 file we created before
+    File file(FILE_NAME, File::ReadOnly);
+
+    std::vector<int> read_data;
+
+    // we get the dataset
+    DataSet dataset = file.getDataSet(DATASET_NAME);
+
+    // we convert the hdf5 dataset to a single dimension vector
+    dataset.read(read_data);
+
+    for (size_t i = 0; i < read_data.size(); ++i) {
+        std::cout << read_data[i] << " ";
+    }
+    std::cout << "\n";
+}
+
+int main(void) {
+
+    try {
+        write_dataset();
+        read_dataset();
+
+    } catch (Exception& err) {
+        // catch and print any HDF5 error
+        std::cerr << err.what() << std::endl;
+    }
+
+    return 0; // successfully terminated
+}
diff --git a/vendor/highfive/src/examples/read_write_vector_dataset_references.cpp b/vendor/highfive/src/examples/read_write_vector_dataset_references.cpp
new file mode 100644
index 00000000..71fb22c9
--- /dev/null
+++ b/vendor/highfive/src/examples/read_write_vector_dataset_references.cpp
@@ -0,0 +1,82 @@
+/*
+ *   Copyright (c), 2021 Blue Brain Project - EPFL
+ *
+ *  Distributed under the Boost Software License, Version 1.0.
+ *    (See accompanying file LICENSE_1_0.txt or copy at
+ *          http://www.boost.org/LICENSE_1_0.txt)
+ *
+ */
+#include <iostream>
+#include <string>
+#include <vector>
+
+#include <highfive/H5DataSet.hpp>
+#include <highfive/H5DataSpace.hpp>
+#include <highfive/H5File.hpp>
+#include <highfive/H5Reference.hpp>
+
+// create a dataset 1D from a vector of int
+void write_dataset() {
+
+    // we create a new hdf5 file
+    HighFive::File file("dataset_integer.h5", HighFive::File::Overwrite);
+
+    // we create a new group
+    HighFive::Group group = file.createGroup("a_group");
+
+    std::vector<int> data(20);
+    std::iota(data.begin(), data.end(), 0);
+
+    // let's create a dataset of native integer with the size of the vector
+    // 'data' inside the group
+    auto dataset = group.createDataSet("source_dataset", data);
+
+    // create a reference to the dataset containing the integers
+    HighFive::Reference ref = HighFive::Reference(group, dataset);
+    std::vector<HighFive::Reference> ref_container{ref};
+
+    // in similar fashion, we store as dataset the vector of reference that we want
+    HighFive::DataSet ref_set = group.createDataSet("reference_dataset", ref_container);
+}
+
+// read our data back
+void read_dataset() {
+    // we open the existing hdf5 file we created before
+    HighFive::File file("dataset_integer.h5", HighFive::File::ReadOnly);
+
+    // we load the group
+    HighFive::Group my_group = file.getGroup("a_group");
+
+    // we load the dataset that contains the reference
+    HighFive::DataSet ref_dataset = my_group.getDataSet("reference_dataset");
+
+    // we load the vector of references
+    std::vector<HighFive::Reference> expected_references;
+    ref_dataset.read(expected_references);
+
+    // we use the stored reference and dereference it to gain access in the integers'
+    // dataset
+    HighFive::DataSet expected_dataset =
+        expected_references[0].dereference<HighFive::DataSet>(my_group);
+
+    // as usual, we load the vector with numbers from the extracted dataset
+    std::vector<int> read_data;
+    expected_dataset.read(read_data);
+
+    // and voila, the payload we excepted
+    for (int i : read_data) {
+        std::cout << i << " ";
+    }
+}
+
+int main() {
+    try {
+        write_dataset();
+        read_dataset();
+
+    } catch (const HighFive::Exception& err) {
+        // catch and print any HDF5 error
+        std::cerr << err.what() << std::endl;
+    }
+    return 0;  // successfully terminated
+}
diff --git a/vendor/highfive/src/examples/renaming_objects.cpp b/vendor/highfive/src/examples/renaming_objects.cpp
new file mode 100644
index 00000000..2bde8126
--- /dev/null
+++ b/vendor/highfive/src/examples/renaming_objects.cpp
@@ -0,0 +1,71 @@
+#include <highfive/H5File.hpp>
+#include <highfive/H5Group.hpp>
+#include <highfive/H5DataSet.hpp>
+#include <highfive/H5DataSpace.hpp>
+#include <highfive/H5Attribute.hpp>
+
+using namespace HighFive;
+
+int main(void)
+{
+    /* We are going to create group in root directory then add
+     * dataset to this group and attach attribute to the dataset.
+     * Then we are trying to get path to the root, group dataset
+     * and the name of the dataset.
+     * Secondly we will move dataset with attached attribute to
+     * some destination path.
+     * To check if dataset object is still valid, we create a
+     * second attribute */
+
+    // Create a new file using the default property lists.
+    File file("names.h5", File::ReadWrite | File::Create | File::Truncate);
+
+    // Create a group
+    Group group = file.createGroup("group");
+
+    // Create a dummy dataset of one single integer
+    DataSet dataset = group.createDataSet("data", DataSpace(1), AtomicType<int>());
+    dataset.write(100);
+
+    // Let's also add a attribute to this dataset
+    std::string string_list("very important DataSet!");
+    Attribute attribute = dataset.createAttribute<std::string>("attribute", DataSpace::From(string_list));
+    attribute.write(string_list);
+
+    // Get path and names
+    std::cout << "root path: " << file.getPath() << std::endl;
+    std::cout << "group path: " << group.getPath() << std::endl;
+    std::cout << "dataset path: " << dataset.getPath() << std::endl;
+    std::cout << "attribute name: " << attribute.getName() << std::endl;
+    std::cout << std::endl;
+
+    // Move dataset with its attribute to another destination path
+    file.rename("/group/data", "/NewGroup/SubGroup/movedData");
+
+    // As you can see to reach destination path new groups were created as well
+    std::cout << "dataset new path: " << dataset.getPath() << std::endl;
+
+    // We can still use moved dataset
+    // Let's create new attribute
+    Attribute attributeNew = dataset.createAttribute<std::string>("attributeNew", DataSpace::From(string_list));
+    attribute.write(string_list);
+    std::cout << "new attribute name: " << attributeNew.getName() << std::endl;
+    std::cout << std::endl;
+
+    // Move the folder with its content to other place
+    file.rename("/NewGroup/SubGroup", "/FinalDestination");
+
+    // Here is the important moment. The old 'dataset' variable tells us
+    // that dataset directory wasn't changed
+    std::cout << "DataSet's path wasn't changed?" << std::endl;
+    std::cout << "dataset path: " << dataset.getPath() << std::endl;
+    std::cout << std::endl;
+
+    // But actually it was moved we just need to update variable
+    dataset = file.getDataSet("/FinalDestination/movedData");
+    std::cout << "Actually it was moved we just need to update it!" << std::endl;
+    std::cout << "dataset path: " << dataset.getPath() << std::endl;
+    std::cout << std::endl;
+
+    file.flush();
+}
diff --git a/vendor/highfive/src/examples/select_by_id_dataset_cpp11.cpp b/vendor/highfive/src/examples/select_by_id_dataset_cpp11.cpp
new file mode 100644
index 00000000..bf71b73a
--- /dev/null
+++ b/vendor/highfive/src/examples/select_by_id_dataset_cpp11.cpp
@@ -0,0 +1,65 @@
+/*
+ *  Copyright (c), 2017, Adrien Devresse
+ *
+ *  Distributed under the Boost Software License, Version 1.0.
+ *    (See accompanying file LICENSE_1_0.txt or copy at
+ *          http://www.boost.org/LICENSE_1_0.txt)
+ *
+ */
+#include <functional>
+#include <iostream>
+#include <string>
+#include <vector>
+
+#include <highfive/H5File.hpp>
+#include <highfive/H5DataSet.hpp>
+#include <highfive/H5DataSpace.hpp>
+
+const std::string FILE_NAME("select_partial_string.h5");
+const std::string DATASET_NAME("message");
+
+// Create a dataset name "dset" of double 4x6
+//
+int main(void) {
+    using namespace HighFive;
+    try {
+        // Create a new file using the default property lists.
+        File file(FILE_NAME, File::ReadWrite | File::Create | File::Truncate);
+
+        {
+            // We have a set of string
+            std::vector<std::string> values = {
+                "Cat", "Dog",  "Hello", "Tree", "World",     "Plane",
+                ", ",  "你好", "Tea",   "Moon", "صباح جميل", "Spaceship",
+            };
+
+            // let's create a dataset
+            DataSet dataset = file.createDataSet<std::string>(
+                DATASET_NAME, DataSpace::From(values));
+
+            // and write them
+            dataset.write(values);
+        }
+
+        {
+            DataSet dataset = file.getDataSet(DATASET_NAME);
+
+            // now let's read back by cherry pick our interesting string
+            std::vector<std::string> result;
+            // we select only element N° 2 and 5
+            dataset.select(ElementSet({2, 4, 6, 7, 6, 10})).read(result);
+
+            // and display it
+            for (auto i : result) {
+                std::cout << i << " ";
+            }
+            std::cout << "\n";
+        }
+
+    } catch (Exception& err) {
+        // catch and print any HDF5 error
+        std::cerr << err.what() << std::endl;
+    }
+
+    return 0; // successfully terminated
+}
diff --git a/vendor/highfive/src/examples/select_partial_dataset_cpp11.cpp b/vendor/highfive/src/examples/select_partial_dataset_cpp11.cpp
new file mode 100644
index 00000000..4a4e5fbf
--- /dev/null
+++ b/vendor/highfive/src/examples/select_partial_dataset_cpp11.cpp
@@ -0,0 +1,57 @@
+/*
+ *  Copyright (c), 2017, Adrien Devresse
+ *
+ *  Distributed under the Boost Software License, Version 1.0.
+ *    (See accompanying file LICENSE_1_0.txt or copy at
+ *          http://www.boost.org/LICENSE_1_0.txt)
+ *
+ */
+#include <functional>
+#include <iostream>
+#include <string>
+#include <vector>
+
+#include <highfive/H5File.hpp>
+#include <highfive/H5DataSet.hpp>
+#include <highfive/H5DataSpace.hpp>
+
+const std::string FILE_NAME("select_partial_example.h5");
+const std::string DATASET_NAME("dset");
+
+// Create a dataset name "dset" of double 4x6
+//
+int main(void) {
+    using namespace HighFive;
+    try {
+        // Create a new file using the default property lists.
+        File file(FILE_NAME, File::ReadWrite | File::Create | File::Truncate);
+
+        // we have some example values in a 2D vector 2x5
+        std::vector<std::vector<double>> values = {
+            {1.0, 2.0, 4.0, 8.0, 16.0}, {32.0, 64.0, 128.0, 256.0, 512.0}};
+
+        // let's create a dataset of this size
+        DataSet dataset =
+            file.createDataSet<double>(DATASET_NAME, DataSpace::From(values));
+        // and write them
+        dataset.write(values);
+
+        // now we read back 2x2 values after an offset of 0x2
+        std::vector<std::vector<double>> result;
+        dataset.select({0, 2}, {2, 2}).read(result);
+
+        // we print out 4 values
+        for (auto i : result) {
+            for (auto j : i) {
+                std::cout << " " << j;
+            }
+            std::cout << "\n";
+        }
+
+    } catch (Exception& err) {
+        // catch and print any HDF5 error
+        std::cerr << err.what() << std::endl;
+    }
+
+    return 0; // successfully terminated
+}


Test patch:
diff --git a/docker/test b/docker/test
new file mode 100755
index 00000000..8a4655cf
--- /dev/null
+++ b/docker/test
@@ -0,0 +1,11 @@
+#!/usr/bin/env bash
+set -ex
+
+HERE=$(dirname $0)
+. $HERE/common
+
+# In case we switch agents between steps
+[ ! -z $(docker images -q $TAG_SHA) ] || docker pull $TAG_SHA
+
+## Just check that we can bring up the container and run something
+docker run -it -w /src --rm $TAG_SHA sketchlib --version
diff --git a/test/run_test.py b/test/run_test.py
index a31bd343..6f418cd0 100755
--- a/test/run_test.py
+++ b/test/run_test.py
@@ -34,20 +34,23 @@
 sys.stderr.write("Distance integration test\n")
 subprocess.run("python ../sketchlib-runner.py query dist test_db --cpus 2", shell=True, check=True) # checks if can be run
 subprocess.run("python ../sketchlib-runner.py query dist test_db -o ppsketch --cpus 2", shell=True, check=True) # checks if can be run
+subprocess.run("python ../sketchlib-runner.py query jaccard test_db_small --cpus 2", shell=True, check=True) # checks if can be run
 subprocess.run("python test-dists.py --ref-db test_db --results ppsketch_ref", shell=True, check=True) # checks results match
 subprocess.run("python ../sketchlib-runner.py query dist test_db_phased --cpus 2", shell=True, check=True) # checks if can be run
 subprocess.run("python test-dists.py --ref-db test_db_phased --results ppsketch_ref_phased", shell=True, check=True) # checks results match
 
+sys.stderr.write("Sparse distance smoke test\n")
+subprocess.run("python ../sketchlib-runner.py query sparse test_db --kNN 2", shell=True, check=True) # checks if can be run
+subprocess.run("python ../sketchlib-runner.py query sparse test_db -o sparse_query --kNN 2", shell=True, check=True) # checks if can be run
+subprocess.run("python ../sketchlib-runner.py query sparse test_db --threshold 0.01", shell=True, check=True) # checks if can be run
+subprocess.run("python ../sketchlib-runner.py query sparse jaccard test_db --kNN 2 --kmer 19", shell=True, check=True) # checks if can be run
+
 sys.stderr.write("Ref v query distance smoke test\n")
 subprocess.run("python ../sketchlib-runner.py sketch -l rlist.txt -o r_db --cpus 2", shell=True, check=True)
 subprocess.run("python ../sketchlib-runner.py sketch -l qlist.txt -o q_db --cpus 2", shell=True, check=True)
 subprocess.run("python ../sketchlib-runner.py query dist r_db q_db.h5", shell=True, check=True) # checks if can be run
 subprocess.run("python ../sketchlib-runner.py query jaccard r_db q_db", shell=True, check=True) # checks if can be run
 
-sys.stderr.write("Sparse distance smoke test\n")
-subprocess.run("python ../sketchlib-runner.py query sparse test_db --kNN 2", shell=True, check=True) # checks if can be run
-subprocess.run("python ../sketchlib-runner.py query sparse test_db -o sparse_query --kNN 2", shell=True, check=True) # checks if can be run
-subprocess.run("python ../sketchlib-runner.py query sparse test_db --threshold 0.01", shell=True, check=True) # checks if can be run
 # Joining
 sys.stderr.write("Join smoke test\n")
 subprocess.run("python ../sketchlib-runner.py sketch -l db1_refs.txt -o db1 --cpus 2", shell=True, check=True)
diff --git a/test/test-dists.py b/test/test-dists.py
index 64cc2912..2843a804 100644
--- a/test/test-dists.py
+++ b/test/test-dists.py
@@ -1,10 +1,22 @@
 import sys
+#sys.path.insert(0, '../build/lib.macosx-10.9-x86_64-3.10')
 import pp_sketchlib
 import numpy as np
 import h5py
 import pickle
 import argparse
 
+def dist_in_tolerance(d1, d2, dist_name, sample_name, tols):
+    if d1 != 0 and d2 != 0:
+            diff = d1 - d2
+            diff_fraction = 2*(diff)/(d1 + d2)
+            if (abs(diff) > tols['warn_diff'] and abs(diff_fraction) > tols['warn_diff_frac']):
+                sys.stderr.write(dist_name + " mismatches for " + sample_name + "\n")
+                sys.stderr.write("expected: " + str(d1) + "; calculated: " + str(d2) + "\n")
+            if (abs(diff) > tols['err_diff'] and abs(diff_fraction) > tols['err_diff_frac']):
+                sys.stderr.write("Difference outside tolerance")
+                sys.exit(1)
+
 def iterDistRows(refSeqs, querySeqs, self=True):
     if self:
         if refSeqs != querySeqs:
@@ -36,6 +48,11 @@ def iterDistRows(refSeqs, querySeqs, self=True):
 
 args = parser.parse_args()
 
+tols = {"warn_diff": args.warn_diff,
+        "warn_diff_frac": args.warn_diff_frac,
+        "err_diff": args.error_diff,
+        "err_diff_frac": args.error_diff_frac}
+
 # Generate distances
 rList = []
 ref = h5py.File(args.ref_db + ".h5", 'r')
@@ -45,13 +62,25 @@ def iterDistRows(refSeqs, querySeqs, self=True):
 db_kmers = ref['sketches/' + rList[0]].attrs['kmers']
 ref.close()
 
-distMat = pp_sketchlib.queryDatabase(args.ref_db, args.ref_db, rList, rList, db_kmers)
-jaccard_dists = pp_sketchlib.queryDatabase(args.ref_db, args.ref_db, rList, rList,
-                                           db_kmers, jaccard = True)
-jaccard_dists_raw = pp_sketchlib.queryDatabase(args.ref_db, args.ref_db, rList, rList,
-                                           db_kmers, jaccard = True,
-                                           random_correct = False)
-distMat = np.hstack((distMat, jaccard_dists, jaccard_dists_raw))
+distMat = pp_sketchlib.queryDatabase(ref_db_name=args.ref_db,
+                                     query_db_name=args.ref_db,
+                                     rList=rList,
+                                     qList=rList,
+                                     klist=db_kmers)
+jaccard_dists = pp_sketchlib.queryDatabase(ref_db_name=args.ref_db,
+                                           query_db_name=args.ref_db,
+                                           rList=rList,
+                                           qList=rList,
+                                           klist=db_kmers,
+                                           jaccard = True)
+jaccard_dists_raw = pp_sketchlib.queryDatabase(ref_db_name=args.ref_db,
+                                               query_db_name=args.ref_db,
+                                               rList=rList,
+                                               qList=rList,
+                                               klist=db_kmers,
+                                               jaccard = True,
+                                               random_correct = False)
+distMat_all = np.hstack((distMat, jaccard_dists, jaccard_dists_raw))
 
 # Read old distances
 with open(args.results + ".pkl", 'rb') as pickle_file:
@@ -59,7 +88,7 @@ def iterDistRows(refSeqs, querySeqs, self=True):
 oldDistMat = np.load(args.results + ".npy")
 oldJaccardDistMat = np.load(args.results + "_jaccard.npy")
 oldRawJaccardDistMat = np.load(args.results + "_raw_jaccard.npy")
-oldDistMat = np.hstack((oldDistMat, oldJaccardDistMat, oldRawJaccardDistMat))
+oldDistMat_all = np.hstack((oldDistMat, oldJaccardDistMat, oldRawJaccardDistMat))
 
 # Check both match
 if (rList_old != rList):
@@ -71,14 +100,41 @@ def iterDistRows(refSeqs, querySeqs, self=True):
 names = iterDistRows(rList, rList, True)
 for i, (ref, query) in enumerate(names):
     for j, (dist) in enumerate(['core', 'accessory'] + [str(x) for x in db_kmers]):
-        if oldDistMat[i, j] != 0 and distMat[i, j] != 0:
-            diff = distMat[i, j] - oldDistMat[i, j]
-            diff_fraction = 2*(diff)/(oldDistMat[i, j] + distMat[i, j])
-            if (abs(diff) > args.warn_diff and abs(diff_fraction) > args.warn_diff_frac):
-                sys.stderr.write(dist + " mismatches for " + ref + "," + query + "\n")
-                sys.stderr.write("expected: " + str(oldDistMat[i, j]) + "; calculated: " + str(distMat[i, j]) + "\n")
-            if (abs(diff) > args.error_diff and abs(diff_fraction) > args.error_diff_frac):
-                sys.stderr.write("Difference outside tolerance")
-                sys.exit(1)
+        dist_in_tolerance(oldDistMat_all[i, j], distMat_all[i, j], dist, ref + "," + query, tols)
+
+# Test sparse queries
+square_core_mat = pp_sketchlib.longToSquare(distVec=distMat[:, [0]])
+
+kNN=3
+sparseDistMat = pp_sketchlib.querySelfSparse(ref_db_name=args.ref_db,
+                                             rList=rList,
+                                             klist=db_kmers,
+                                             kNN=kNN)
+sparse_knn = pp_sketchlib.sparsifyDists(distMat=square_core_mat, distCutoff=0, kNN=kNN)
+if (sparseDistMat[0] != sparse_knn[0] or
+    sparseDistMat[1] != sparse_knn[1]):
+    sys.stderr.write("Sparse distances (kNN) mismatching\n")
+    print(sparseDistMat)
+    print(sparse_knn)
+    sys.exit(1)
+
+for idx, (d1, d2) in enumerate(zip(sparseDistMat[2], sparseDistMat[2])):
+    dist_in_tolerance(d1, d2, "sparse distances (kNN)", str(idx), tols)
+
+cutoff=0.01
+sparseDistMat = pp_sketchlib.querySelfSparse(ref_db_name=args.ref_db,
+                                             rList=rList,
+                                             klist=db_kmers,
+                                             dist_cutoff=cutoff)
+sparse_threshold = pp_sketchlib.sparsifyDists(distMat=square_core_mat, distCutoff=cutoff, kNN=0)
+if (sparseDistMat[0] != sparse_threshold[0] or
+    sparseDistMat[1] != sparse_threshold[1]):
+    sys.stderr.write("Sparse distances (cutoff) mismatching\n")
+    print(sparseDistMat)
+    print(sparse_threshold)
+    sys.exit(1)
+
+for idx, (d1, d2) in enumerate(zip(sparseDistMat[2], sparseDistMat[2])):
+    dist_in_tolerance(d1, d2, "sparse distances (cutoff)", str(idx), tols)
 
 sys.exit(0)
diff --git a/test/test-matrix.py b/test/test-matrix.py
index ad3364c3..3cf227fe 100644
--- a/test/test-matrix.py
+++ b/test/test-matrix.py
@@ -3,14 +3,14 @@
 from math import sqrt
 
 # testing without install
-#sys.path.insert(0, '../build/lib.macosx-10.9-x86_64-3.8')
+#sys.path.insert(0, '../build/lib.macosx-10.9-x86_64-3.10')
 import pp_sketchlib
 try:
     from pp_sketch.matrix import sparsify
 except ImportError as e:
     from scipy.sparse import coo_matrix
     def sparsify(distMat, cutoff, kNN, threads):
-        sparse_coordinates = pp_sketchlib.sparsifyDists(distMat,
+        sparse_coordinates = pp_sketchlib.sparsifyDists(distMat=distMat,
                                                         distCutoff=cutoff,
                                                         kNN=kNN)
         sparse_scipy = coo_matrix((sparse_coordinates[2],
@@ -36,8 +36,10 @@ def check_res(res, expected):
 qr_mat = np.array([10, 11, 12, 13, 14, 15, 16, 17], dtype=np.float32)
 # NB r_idx is inner/fastest: q0r0, q0r1, q0r2, q0r3, q1r0, q1r1, q1r2, q1r3
 
-square1 = pp_sketchlib.longToSquare(rr_mat, 2)
-square2 = pp_sketchlib.longToSquareMulti(rr_mat, qr_mat, qq_mat)
+square1 = pp_sketchlib.longToSquare(distVec=rr_mat, num_threads=2)
+square2 = pp_sketchlib.longToSquareMulti(distVec=rr_mat,
+                                         query_ref_distVec=qr_mat,
+                                         query_query_distVec=qq_mat)
 
 square1_res = np.array([[0, 1, 2, 3],
                         [1, 0, 4, 5],
@@ -55,7 +57,7 @@ def check_res(res, expected):
 check_res(square1_res, square1)
 check_res(square2_res, square2)
 
-check_res(pp_sketchlib.squareToLong(square1_res, 2), rr_mat)
+check_res(pp_sketchlib.squareToLong(distMat=square1_res, num_threads=2), rr_mat)
 
 # sparsification
 sparse1 = sparsify(square2_res, cutoff=5, kNN=0, threads=2)
diff --git a/vendor/highfive/tests/test_dependent_library/CMakeLists.txt b/vendor/highfive/tests/test_dependent_library/CMakeLists.txt
new file mode 100644
index 00000000..1231a141
--- /dev/null
+++ b/vendor/highfive/tests/test_dependent_library/CMakeLists.txt
@@ -0,0 +1,42 @@
+# This is a sample library to test integration via add_subdirectory and CMakeConfig
+cmake_minimum_required(VERSION 3.1)
+
+project(test_project VERSION 0.1)
+
+if(NOT DEFINED CMAKE_CXX_STANDARD)
+  set(CMAKE_CXX11_STANDARD_COMPILE_OPTION "-std=c++11")  # For come compilers under cmake 3.1
+  set(CMAKE_CXX_STANDARD 11)
+  set(CMAKE_CXX_STANDARD_REQUIRED ON)
+  set(CMAKE_CXX_EXTENSIONS OFF)
+endif()
+
+option(USE_BUNDLED_HIGHFIVE "Use highfive from deps folder. Otherwise must be installed" ON)
+
+if(USE_BUNDLED_HIGHFIVE)
+    add_subdirectory("deps/HighFive" EXCLUDE_FROM_ALL)
+else()
+    find_package(HighFive REQUIRED)
+endif()
+
+add_library(simpleton SHARED "src/simpleton.cpp" "src/otherton.cpp")
+target_include_directories(simpleton
+    PUBLIC
+    $<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/include>
+    $<INSTALL_INTERFACE:include>)
+target_link_libraries(simpleton PUBLIC HighFive)
+set_property(TARGET simpleton PROPERTY POSITION_INDEPENDENT_CODE ON)
+
+add_library(otherton STATIC "src/simpleton.cpp" "src/otherton.cpp")
+target_include_directories(otherton
+    PUBLIC
+    $<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/include>
+    $<INSTALL_INTERFACE:include>)
+target_link_libraries(otherton PUBLIC HighFive)
+set_property(TARGET otherton PROPERTY POSITION_INDEPENDENT_CODE OFF)
+
+install(
+    TARGETS simpleton otherton
+    EXPORT simpletonTarget
+    DESTINATION lib
+    ARCHIVE DESTINATION lib)
+install(EXPORT simpletonTarget DESTINATION lib)
diff --git a/vendor/highfive/tests/test_dependent_library/deps/.gitignore b/vendor/highfive/tests/test_dependent_library/deps/.gitignore
new file mode 100644
index 00000000..5e7d2734
--- /dev/null
+++ b/vendor/highfive/tests/test_dependent_library/deps/.gitignore
@@ -0,0 +1,4 @@
+# Ignore everything in this directory
+*
+# Except this file
+!.gitignore
diff --git a/vendor/highfive/tests/test_dependent_library/include/simpleton.hpp b/vendor/highfive/tests/test_dependent_library/include/simpleton.hpp
new file mode 100644
index 00000000..0684294d
--- /dev/null
+++ b/vendor/highfive/tests/test_dependent_library/include/simpleton.hpp
@@ -0,0 +1,27 @@
+#ifndef H5_TEST_SIMPLETON_HPP
+#define H5_TEST_SIMPLETON_HPP
+
+// Include all headers here to catch any missing `inline` statements, since
+// they will be included by two different compilation units.
+#include <highfive/H5Attribute.hpp>
+#include <highfive/H5DataSet.hpp>
+#include <highfive/H5DataSpace.hpp>
+#include <highfive/H5DataType.hpp>
+#include <highfive/H5Easy.hpp>
+#include <highfive/H5Exception.hpp>
+#include <highfive/H5File.hpp>
+#include <highfive/H5FileDriver.hpp>
+#include <highfive/H5Group.hpp>
+#include <highfive/H5Object.hpp>
+#include <highfive/H5PropertyList.hpp>
+#include <highfive/H5Reference.hpp>
+#include <highfive/H5Selection.hpp>
+#include <highfive/H5Utility.hpp>
+
+// Boost should always be found in this setup
+#include <boost/numeric/ublas/matrix.hpp>
+
+void function(const HighFive::Object& obj);
+void other_function(const boost::numeric::ublas::matrix<double>& m);
+
+#endif
diff --git a/vendor/highfive/tests/test_dependent_library/src/otherton.cpp b/vendor/highfive/tests/test_dependent_library/src/otherton.cpp
new file mode 100644
index 00000000..3e10a363
--- /dev/null
+++ b/vendor/highfive/tests/test_dependent_library/src/otherton.cpp
@@ -0,0 +1,5 @@
+#include "simpleton.hpp"
+
+void other_function(const boost::numeric::ublas::matrix<double>& m) {
+    m(0, 0) * 0.0;
+}
diff --git a/vendor/highfive/tests/test_dependent_library/src/simpleton.cpp b/vendor/highfive/tests/test_dependent_library/src/simpleton.cpp
new file mode 100644
index 00000000..12cef5bf
--- /dev/null
+++ b/vendor/highfive/tests/test_dependent_library/src/simpleton.cpp
@@ -0,0 +1,9 @@
+#include <stdexcept>
+
+#include "simpleton.hpp"
+
+void function(const HighFive::Object& obj) {
+    if (!obj.isValid()) {
+        throw std::exception();
+    }
+}
diff --git a/vendor/highfive/tests/test_project_integration.sh b/vendor/highfive/tests/test_project_integration.sh
new file mode 100644
index 00000000..386a052b
--- /dev/null
+++ b/vendor/highfive/tests/test_project_integration.sh
@@ -0,0 +1,51 @@
+#!/bin/sh
+set -xeo pipefail
+cd "$( dirname "${BASH_SOURCE[0]}")"  # cd here
+
+BUILDDIR="${PWD}/build"
+ROOT="${PWD}/.."
+TESTDIR="${PWD}"
+
+test_install() {
+    local project="${1}"
+    local builddir="${BUILDDIR}/${project}/${2}"
+    shift
+    shift
+    ln -s ../../.. "${TESTDIR}/${project}/deps/HighFive"
+    rm -rf "${builddir}"
+    mkdir -p "${builddir}"
+    pushd "${builddir}"
+    cmake "${TESTDIR}/${project}" "$@"
+    make VERBOSE=1
+    ctest
+    popd
+    rm "${TESTDIR}/${project}/deps/HighFive"
+}
+
+rm -rf "${BUILDDIR}/highfive"
+mkdir -p "${BUILDDIR}/highfive"
+pushd "${BUILDDIR}/highfive"
+cmake "${ROOT}" \
+    -DHIGHFIVE_EXAMPLES=OFF \
+    -DHIGHFIVE_UNIT_TESTS=OFF \
+    -DCMAKE_INSTALL_PREFIX="${PWD}/install"
+make install
+popd
+
+for project in test_project test_dependent_library; do
+    # Case 1. Base case: include subdirectory
+    test_install "${project}" subdir
+
+    # Case 2. We use an install dir and all deps configuration
+    # Install highfive (no tests required)
+    test_install "${project}" reuse_deps \
+        -DUSE_BUNDLED_HIGHFIVE=NO \
+        -DHIGHFIVE_USE_INSTALL_DEPS=YES \
+        -DCMAKE_PREFIX_PATH="${BUILDDIR}/highfive/install"
+            #
+    # Case 3. We redetect-dependencies
+    test_install "${project}" install_new_deps \
+        -DUSE_BUNDLED_HIGHFIVE=NO \
+        -DHIGHFIVE_USE_INSTALL_DEPS=NO \
+        -DCMAKE_PREFIX_PATH="${BUILDDIR}/highfive/install"
+done
diff --git a/vendor/highfive/tests/unit/CMakeLists.txt b/vendor/highfive/tests/unit/CMakeLists.txt
new file mode 100644
index 00000000..e1ddd2ca
--- /dev/null
+++ b/vendor/highfive/tests/unit/CMakeLists.txt
@@ -0,0 +1,43 @@
+if(NOT HIGHFIVE_UNIT_TESTS)
+  return()
+endif()
+
+# NOTE: These unit tests expect linking with boost test framework lib
+if(NOT Boost_USE_STATIC_LIBS)
+  add_definitions(-DBOOST_TEST_DYN_LINK=TRUE)
+endif()
+
+## Base tests
+foreach(test_name tests_high_five_base tests_high_five_multi_dims tests_high_five_easy)
+  add_executable(${test_name} "${test_name}.cpp")
+  target_link_libraries(${test_name} ${Boost_UNIT_TEST_FRAMEWORK_LIBRARIES} HighFive)
+  add_test(NAME ${test_name} COMMAND ${test_name})
+endforeach()
+
+
+if(HIGHFIVE_PARALLEL_HDF5)
+
+  include(TestHelpers)
+
+  ## parallel MPI tests
+  file(GLOB tests_parallel_src "*parallel.cpp")
+
+  add_executable(tests_parallel_bin ${tests_parallel_src})
+  target_link_libraries(tests_parallel_bin ${Boost_UNIT_TEST_FRAMEWORK_LIBRARIES} HighFive)
+  add_test(NAME tests_parallel COMMAND ${TEST_MPI_EXEC_PREFIX_DEFAULT} -n 2
+    ${CMAKE_CURRENT_BINARY_DIR}/tests_parallel_bin)
+
+endif()
+
+
+option(HIGHFIVE_TEST_SINGLE_INCLUDES "Enable testing single includes" FALSE)
+
+if(HIGHFIVE_TEST_SINGLE_INCLUDES)
+    file(GLOB public_headers LIST_DIRECTORIES false RELATIVE ${PROJECT_SOURCE_DIR}/include ${PROJECT_SOURCE_DIR}/include/highfive/*.hpp)
+    foreach(PUBLIC_HEADER ${public_headers})
+        get_filename_component(CLASS_NAME ${PUBLIC_HEADER} NAME_WE)
+        configure_file(tests_import_public_headers.cpp "tests_${CLASS_NAME}.cpp" @ONLY)
+        add_executable("tests_include_${CLASS_NAME}" "${CMAKE_CURRENT_BINARY_DIR}/tests_${CLASS_NAME}.cpp")
+        target_link_libraries("tests_include_${CLASS_NAME}" HighFive)
+    endforeach()
+endif()
diff --git a/vendor/highfive/tests/unit/tests_high_five.hpp b/vendor/highfive/tests/unit/tests_high_five.hpp
new file mode 100644
index 00000000..c31b4b38
--- /dev/null
+++ b/vendor/highfive/tests/unit/tests_high_five.hpp
@@ -0,0 +1,169 @@
+/*
+ *  Copyright (c), 2017-2019, Blue Brain Project - EPFL
+ *
+ *  Distributed under the Boost Software License, Version 1.0.
+ *    (See accompanying file LICENSE_1_0.txt or copy at
+ *          http://www.boost.org/LICENSE_1_0.txt)
+ *
+ */
+
+#include <complex>
+#include <random>
+#include <string>
+#include <vector>
+#include <boost/mpl/list.hpp>
+
+// Since, 1.59: semicolon has been removed from the end of the BOOST_GLOBAL_FIXTURE
+// https://github.com/boostorg/test/commit/3f7216db3db2e11a768d8d0c8bb18632f106c466
+#if BOOST_VERSION >= 105900
+#define BOOST_GLOBAL_FIXTURE_END ;
+#else
+#define BOOST_GLOBAL_FIXTURE_END
+#endif
+
+using complex = std::complex<double>;
+
+typedef boost::mpl::list<float, double> floating_numerics_test_types;
+
+typedef boost::mpl::list<int, unsigned int, long, unsigned long, unsigned char, char,
+                         float, double, long long, unsigned long long, complex>
+    numerical_test_types;
+
+typedef boost::mpl::list<int, unsigned int, long, unsigned long, unsigned char, char,
+                         float, double>
+    dataset_test_types;
+
+
+template <typename T, typename Func>
+void fillVec(std::vector<std::vector<T>>& v, std::vector<size_t> dims, const Func& f) {
+    v.resize(dims[0]);
+    dims.erase(dims.begin());
+    for (auto& subvec : v) {
+        fillVec(subvec, dims, f);
+    }
+}
+
+template <typename T, typename Func>
+void fillVec(std::vector<T>& v, std::vector<size_t> dims, const Func& f) {
+    v.resize(dims[0]);
+    std::generate(v.begin(), v.end(), f);
+}
+
+
+template <typename T>
+bool checkLength(const std::vector<T>& v, std::vector<size_t> dims) {
+    return (dims.size() == 1 && v.size() == dims[0]);
+}
+
+template <typename T>
+bool checkLength(const std::vector<std::vector<T>>& v, std::vector<size_t> dims) {
+    size_t dim0 = dims[0];
+    dims.erase(dims.begin());
+    if (v.size() != dim0) {
+        return false;
+    }
+    return checkLength(v[0], dims);
+}
+
+
+template <typename T, typename Func>
+void generate2D(T* table, size_t x, size_t y, Func& func) {
+    for (size_t i = 0; i < x; i++) {
+        for (size_t j = 0; j < y; j++) {
+            table[i][j] = func();
+        }
+    }
+}
+
+
+template <typename T>
+struct ContentGenerate {
+    ContentGenerate()
+        : _init(0)
+        , _inc(T(1) + T(1) / T(10)) {}
+
+    T operator()() {
+        T ret = _init;
+        _init = static_cast<T>(_init + _inc);
+        return ret;
+    }
+
+    T _init, _inc;
+};
+
+template <>
+ContentGenerate<complex>::ContentGenerate()
+    : _init(0, 0)
+    , _inc(complex(1, 1) + complex(1, 1) / complex(10)) {}
+
+template <>
+struct ContentGenerate<char> {
+    ContentGenerate()
+        : _init('a') {}
+
+    char operator()() {
+        char ret = _init;
+        if (++_init >= ('a' + 26))
+            _init = 'a';
+        return ret;
+    }
+
+    char _init;
+};
+
+template <>
+struct ContentGenerate<std::string> {
+    ContentGenerate() {}
+
+    std::string operator()() {
+        ContentGenerate<char> gen;
+        std::string random_string;
+        std::mt19937_64 rgen;
+        rgen.seed(88);
+        std::uniform_int_distribution<unsigned> int_dist(0, 1000);
+        const size_t size_string = int_dist(rgen);
+
+        random_string.resize(size_string);
+        std::generate(random_string.begin(), random_string.end(), gen);
+        return random_string;
+    }
+};
+
+
+template <typename T>
+inline std::string typeNameHelper() {
+    std::string name = typeid(T).name();
+#if defined(WIN32)
+    //Replace illegal windows file path characters
+    std::replace(std::begin(name), std::end(name), ' ', '_');
+    std::replace(std::begin(name), std::end(name), '<', '_');
+    std::replace(std::begin(name), std::end(name), '>', '_');
+    std::replace(std::begin(name), std::end(name), ':', '_');
+#endif
+    return name;
+}
+
+template <typename ElemT, typename DataT>
+inline HighFive::DataSet
+readWriteDataset(const DataT& ndvec,
+                 DataT& result,
+                 const size_t ndims,
+                 const std::string& struct_t) {
+    using namespace HighFive;
+    const std::string DATASET_NAME("dset");
+
+    std::ostringstream filename;
+    filename << "h5_rw_" << struct_t << "_" << ndims << "d_"
+             << typeNameHelper<ElemT>() << "_test.h5";
+
+    // Create a new file using the default property lists.
+    File file(filename.str(), File::Truncate);
+
+    // Create a dataset with type T points
+    DataSet dataset = file.createDataSet<ElemT>(DATASET_NAME, DataSpace::From(ndvec));
+    dataset.write(ndvec);
+
+    dataset.read(result);
+    return dataset;
+}
+
diff --git a/vendor/highfive/tests/unit/tests_high_five_base.cpp b/vendor/highfive/tests/unit/tests_high_five_base.cpp
new file mode 100644
index 00000000..c3929f21
--- /dev/null
+++ b/vendor/highfive/tests/unit/tests_high_five_base.cpp
@@ -0,0 +1,2046 @@
+/*
+ *  Copyright (c), 2017-2019, Blue Brain Project - EPFL
+ *
+ *  Distributed under the Boost Software License, Version 1.0.
+ *    (See accompanying file LICENSE_1_0.txt or copy at
+ *          http://www.boost.org/LICENSE_1_0.txt)
+ *
+ */
+
+#include <algorithm>
+#include <cstdio>
+#include <cstdlib>
+#include <ctime>
+#include <iostream>
+#include <memory>
+#include <random>
+#include <string>
+#include <typeinfo>
+#include <vector>
+
+#include <highfive/H5DataSet.hpp>
+#include <highfive/H5DataSpace.hpp>
+#include <highfive/H5File.hpp>
+#include <highfive/H5Group.hpp>
+#include <highfive/H5Reference.hpp>
+#include <highfive/H5Utility.hpp>
+
+#define BOOST_TEST_MAIN HighFiveTestBase
+#include <boost/test/unit_test.hpp>
+
+#include "tests_high_five.hpp"
+
+using namespace HighFive;
+
+BOOST_AUTO_TEST_CASE(HighFiveBasic) {
+    const std::string FILE_NAME("h5tutr_dset.h5");
+    const std::string DATASET_NAME("dset");
+
+    // Create a new file using the default property lists.
+    File file(FILE_NAME, File::ReadWrite | File::Create | File::Truncate);
+
+    BOOST_CHECK_EQUAL(file.getName(), FILE_NAME);
+
+    // Create the data space for the dataset.
+    std::vector<size_t> dims{4, 6};
+
+    DataSpace dataspace(dims);
+
+    // check if the dataset exist
+    bool dataset_exist = file.exist(DATASET_NAME + "_double");
+    BOOST_CHECK(!dataset_exist);
+
+    // Create a dataset with double precision floating points
+    DataSet dataset_double = file.createDataSet(DATASET_NAME + "_double", dataspace,
+                                                AtomicType<double>());
+
+    BOOST_CHECK_EQUAL(file.getObjectName(0), DATASET_NAME + "_double");
+
+    {
+        // check if it exist again
+        dataset_exist = file.exist(DATASET_NAME + "_double");
+        BOOST_CHECK_EQUAL(dataset_exist, true);
+
+        // and also try to recreate it to the sake of exception testing
+        SilenceHDF5 silencer;
+        BOOST_CHECK_THROW(
+            {
+                DataSet fail_duplicated = file.createDataSet(
+                    DATASET_NAME + "_double", dataspace, AtomicType<double>());
+            },
+            DataSetException);
+    }
+
+    DataSet dataset_size_t = file.createDataSet<size_t>(DATASET_NAME + "_size_t",
+                                                        dataspace);
+}
+
+BOOST_AUTO_TEST_CASE(HighFiveSilence) {
+    // Setting up a buffer for stderr so we can detect if the stack trace
+    // was disabled
+    fflush(stderr);
+    char buffer[1024];
+    memset(buffer, 0, sizeof(char) * 1024);
+    setvbuf(stderr, buffer, _IOLBF, 1023);
+
+    try {
+        SilenceHDF5 silence;
+        File file("nonexistent", File::ReadOnly);
+    } catch (const FileException&) {
+    }
+    BOOST_CHECK_EQUAL(buffer[0], '\0');
+
+    // restore the dyn allocated buffer
+    // or using stderr will segfault when buffer get out of scope
+    fflush(stderr);
+    setvbuf(stderr, NULL, _IONBF, 0);
+}
+
+BOOST_AUTO_TEST_CASE(HighFiveOpenMode) {
+    const std::string FILE_NAME("openmodes.h5");
+    const std::string DATASET_NAME("dset");
+
+    std::remove(FILE_NAME.c_str());
+
+    SilenceHDF5 silencer;
+
+    // Attempt open file only ReadWrite should fail (wont create)
+    BOOST_CHECK_THROW({ File file(FILE_NAME, File::ReadWrite); }, FileException);
+
+    // But with Create flag should be fine
+    { File file(FILE_NAME, File::ReadWrite | File::Create); }
+
+    // But if its there and exclusive is given, should fail
+    BOOST_CHECK_THROW({ File file(FILE_NAME, File::ReadWrite | File::Excl); },
+                      FileException);
+    // ReadWrite and Excl flags are fine together (posix)
+    std::remove(FILE_NAME.c_str());
+    { File file(FILE_NAME, File::ReadWrite | File::Excl); }
+    // All three are fine as well (as long as the file does not exist)
+    std::remove(FILE_NAME.c_str());
+    { File file(FILE_NAME, File::ReadWrite | File::Create | File::Excl); }
+
+    // Just a few combinations are incompatible, detected by hdf5lib
+    BOOST_CHECK_THROW({ File file(FILE_NAME, File::Truncate | File::Excl); },
+                      FileException);
+
+    std::remove(FILE_NAME.c_str());
+    BOOST_CHECK_THROW({ File file(FILE_NAME, File::Truncate | File::Excl); },
+                      FileException);
+
+    // But in most cases we will truncate and that should always work
+    { File file(FILE_NAME, File::Truncate); }
+    std::remove(FILE_NAME.c_str());
+    { File file(FILE_NAME, File::Truncate); }
+
+    // Last but not least, defaults should be ok
+    { File file(FILE_NAME); }     // ReadOnly
+    { File file(FILE_NAME, 0); }  // force empty-flags, does open without flags
+}
+
+
+BOOST_AUTO_TEST_CASE(HighFiveGroupAndDataSetDefaultCtr) {
+    const std::string FILE_NAME("h5_group_test.h5");
+    const std::string DATASET_NAME("dset");
+    File file(FILE_NAME, File::Truncate);
+    auto ds = file.createDataSet(DATASET_NAME, std::vector<int>{1, 2, 3, 4, 5});
+
+    DataSet d2;  // deprecated as it constructs unsafe objects
+    // d2.getFile();  // runtime error
+    BOOST_CHECK_EQUAL(d2.isValid(), false);
+    d2 = ds;  // copy
+    BOOST_CHECK_EQUAL(d2.isValid(), true);
+}
+
+
+BOOST_AUTO_TEST_CASE(HighFiveGroupAndDataSet) {
+    const std::string FILE_NAME("h5_group_test.h5");
+    const std::string DATASET_NAME("dset");
+    const std::string CHUNKED_DATASET_NAME("chunked_dset");
+    const std::string CHUNKED_DATASET_SMALL_NAME("chunked_dset_small");
+    const std::string GROUP_NAME1("/group1");
+    const std::string GROUP_NAME2("group2");
+    const std::string GROUP_NESTED_NAME("group_nested");
+
+    {
+        // Create a new file using the default property lists.
+        File file(FILE_NAME, File::ReadWrite | File::Create | File::Truncate);
+
+        // absolute group
+        file.createGroup(GROUP_NAME1);
+        // nested group absolute
+        file.createGroup(GROUP_NAME1 + "/" + GROUP_NESTED_NAME);
+        // relative group
+        Group g1 = file.createGroup(GROUP_NAME2);
+        // relative group
+        Group nested = g1.createGroup(GROUP_NESTED_NAME);
+
+        // Create the data space for the dataset.
+        std::vector<size_t> dims{4, 6};
+
+        DataSpace dataspace(dims);
+
+        DataSet dataset_absolute = file.createDataSet(
+            GROUP_NAME1 + "/" + GROUP_NESTED_NAME + "/" + DATASET_NAME, dataspace,
+            AtomicType<double>());
+
+        DataSet dataset_relative = nested.createDataSet(DATASET_NAME, dataspace,
+                                                        AtomicType<double>());
+
+        DataSetCreateProps goodChunking;
+        goodChunking.add(Chunking(std::vector<hsize_t>{2, 2}));
+        DataSetAccessProps cacheConfig;
+        cacheConfig.add(Caching(13, 1024, 0.5));
+
+        // will fail because exceeds dimensions
+        DataSetCreateProps badChunking0;
+        badChunking0.add(Chunking(std::vector<hsize_t>{10, 10}));
+
+        DataSetCreateProps badChunking1;
+        badChunking1.add(Chunking(std::vector<hsize_t>{1, 1, 1}));
+
+        {
+            SilenceHDF5 silencer;
+            BOOST_CHECK_THROW(file.createDataSet(CHUNKED_DATASET_NAME, dataspace,
+                                                 AtomicType<double>(), badChunking0),
+                              DataSetException);
+
+            BOOST_CHECK_THROW(file.createDataSet(CHUNKED_DATASET_NAME, dataspace,
+                                                 AtomicType<double>(), badChunking1),
+                              DataSetException);
+        }
+
+        // here we use the other signature
+        DataSet dataset_chunked = file.createDataSet<float>(
+            CHUNKED_DATASET_NAME, dataspace, goodChunking, cacheConfig);
+
+        // Here we resize to smaller than the chunking size
+        DataSet dataset_chunked_small = file.createDataSet<float>(
+            CHUNKED_DATASET_SMALL_NAME, dataspace, goodChunking);
+
+        dataset_chunked_small.resize({1, 1});
+    }
+    // read it back
+    {
+        File file(FILE_NAME, File::ReadOnly);
+        Group g1 = file.getGroup(GROUP_NAME1);
+        Group g2 = file.getGroup(GROUP_NAME2);
+        Group nested_group2 = g2.getGroup(GROUP_NESTED_NAME);
+
+        DataSet dataset_absolute = file.getDataSet(GROUP_NAME1 + "/" + GROUP_NESTED_NAME +
+                                                   "/" + DATASET_NAME);
+        BOOST_CHECK_EQUAL(4, dataset_absolute.getSpace().getDimensions()[0]);
+
+        DataSet dataset_relative = nested_group2.getDataSet(DATASET_NAME);
+        BOOST_CHECK_EQUAL(4, dataset_relative.getSpace().getDimensions()[0]);
+
+        DataSetAccessProps accessProps;
+        accessProps.add(Caching(13, 1024, 0.5));
+        DataSet dataset_chunked = file.getDataSet(CHUNKED_DATASET_NAME, accessProps);
+        BOOST_CHECK_EQUAL(4, dataset_chunked.getSpace().getDimensions()[0]);
+
+        DataSet dataset_chunked_small = file.getDataSet(CHUNKED_DATASET_SMALL_NAME);
+        BOOST_CHECK_EQUAL(1, dataset_chunked_small.getSpace().getDimensions()[0]);
+    }
+}
+
+BOOST_AUTO_TEST_CASE(HighFiveExtensibleDataSet) {
+    const std::string FILE_NAME("create_extensible_dataset_example.h5");
+    const std::string DATASET_NAME("dset");
+    constexpr double t1[3][1] = {{2.0}, {2.0}, {4.0}};
+    constexpr double t2[1][3] = {{4.0, 8.0, 6.0}};
+
+    {
+        // Create a new file using the default property lists.
+        File file(FILE_NAME, File::ReadWrite | File::Create | File::Truncate);
+
+        // Create a dataspace with initial shape and max shape
+        DataSpace dataspace = DataSpace({4, 5}, {17, DataSpace::UNLIMITED});
+
+        // Use chunking
+        DataSetCreateProps props;
+        props.add(Chunking(std::vector<hsize_t>{2, 2}));
+
+        // Create the dataset
+        DataSet dataset = file.createDataSet(DATASET_NAME, dataspace,
+                                             AtomicType<double>(), props);
+
+        // Write into the initial part of the dataset
+        dataset.select({0, 0}, {3, 1}).write(t1);
+
+        // Resize the dataset to a larger size
+        dataset.resize({4, 6});
+
+        BOOST_CHECK_EQUAL(4, dataset.getSpace().getDimensions()[0]);
+        BOOST_CHECK_EQUAL(6, dataset.getSpace().getDimensions()[1]);
+
+        // Write into the new part of the dataset
+        dataset.select({3, 3}, {1, 3}).write(t2);
+
+        SilenceHDF5 silencer;
+        // Try resize out of bounds
+        BOOST_CHECK_THROW(dataset.resize({18, 1}), DataSetException);
+        // Try resize invalid dimensions
+        BOOST_CHECK_THROW(dataset.resize({1, 2, 3}), DataSetException);
+    }
+
+    // read it back
+    {
+        File file(FILE_NAME, File::ReadOnly);
+
+        DataSet dataset_absolute = file.getDataSet("/" + DATASET_NAME);
+        const auto dims = dataset_absolute.getSpace().getDimensions();
+        double values[4][6];
+        dataset_absolute.read(values);
+        BOOST_CHECK_EQUAL(4, dims[0]);
+        BOOST_CHECK_EQUAL(6, dims[1]);
+
+        BOOST_CHECK_EQUAL(t1[0][0], values[0][0]);
+        BOOST_CHECK_EQUAL(t1[1][0], values[1][0]);
+        BOOST_CHECK_EQUAL(t1[2][0], values[2][0]);
+
+        BOOST_CHECK_EQUAL(t2[0][0], values[3][3]);
+        BOOST_CHECK_EQUAL(t2[0][1], values[3][4]);
+        BOOST_CHECK_EQUAL(t2[0][2], values[3][5]);
+    }
+}
+
+BOOST_AUTO_TEST_CASE(HighFiveRefCountMove) {
+    const std::string FILE_NAME("h5_ref_count_test.h5");
+    const std::string DATASET_NAME("dset");
+    const std::string GROUP_NAME1("/group1");
+    const std::string GROUP_NAME2("/group2");
+
+    // Create a new file using the default property lists.
+    File file(FILE_NAME, File::ReadWrite | File::Create | File::Truncate);
+
+    std::unique_ptr<DataSet> d1_ptr;
+    std::unique_ptr<Group> g_ptr;
+
+    {
+        // create group
+        Group g1 = file.createGroup(GROUP_NAME1);
+
+        // override object
+        g1 = file.createGroup(GROUP_NAME2);
+
+        // Create the data space for the dataset.
+        std::vector<size_t> dims = {10, 10};
+
+        DataSpace dataspace(dims);
+
+        DataSet d1 = file.createDataSet(GROUP_NAME1 + DATASET_NAME, dataspace,
+                                        AtomicType<double>());
+
+        double values[10][10] = {{0}};
+        values[5][0] = 1;
+        d1.write(values);
+
+        // force move
+        d1_ptr.reset(new DataSet(std::move(d1)));
+
+        // force copy
+        g_ptr.reset(new Group(g1));
+    }
+    // read it back
+    {
+        DataSet d2(std::move(*d1_ptr));
+        d1_ptr.reset();
+
+        double values[10][10];
+        d2.read(values);
+
+        for (std::size_t i = 0; i < 10; ++i) {
+            for (std::size_t j = 0; j < 10; ++j) {
+                double v = values[i][j];
+
+                if (i == 5 && j == 0) {
+                    BOOST_CHECK_EQUAL(v, 1);
+                } else {
+                    BOOST_CHECK_EQUAL(v, 0);
+                }
+            }
+        }
+
+        // force copy
+        Group g2 = *g_ptr;
+
+        // add a subgroup
+        g2.createGroup("blabla");
+    }
+}
+
+BOOST_AUTO_TEST_CASE(HighFiveSimpleListing) {
+    const std::string FILE_NAME("h5_list_test.h5");
+    const std::string GROUP_NAME_CORE("group_name");
+    const std::string GROUP_NESTED_NAME("/group_nested");
+
+    // Create a new file using the default property lists.
+    File file(FILE_NAME, File::ReadWrite | File::Create | File::Truncate);
+
+    {
+        // absolute group
+        for (int i = 0; i < 2; ++i) {
+            std::ostringstream ss;
+            ss << "/" << GROUP_NAME_CORE << "_" << i;
+            file.createGroup(ss.str());
+        }
+
+        size_t n_elem = file.getNumberObjects();
+        BOOST_CHECK_EQUAL(2, n_elem);
+
+        std::vector<std::string> elems = file.listObjectNames();
+        BOOST_CHECK_EQUAL(2, elems.size());
+        std::vector<std::string> reference_elems;
+        for (int i = 0; i < 2; ++i) {
+            std::ostringstream ss;
+            ss << GROUP_NAME_CORE << "_" << i;
+            reference_elems.push_back(ss.str());
+        }
+
+        BOOST_CHECK_EQUAL_COLLECTIONS(elems.begin(), elems.end(), reference_elems.begin(),
+                                      reference_elems.end());
+    }
+
+    {
+        file.createGroup(GROUP_NESTED_NAME);
+        Group g_nest = file.getGroup(GROUP_NESTED_NAME);
+
+        for (int i = 0; i < 50; ++i) {
+            std::ostringstream ss;
+            ss << GROUP_NAME_CORE << "_" << i;
+            g_nest.createGroup(ss.str());
+        }
+
+        size_t n_elem = g_nest.getNumberObjects();
+        BOOST_CHECK_EQUAL(50, n_elem);
+
+        std::vector<std::string> elems = g_nest.listObjectNames();
+        BOOST_CHECK_EQUAL(50, elems.size());
+        std::vector<std::string> reference_elems;
+
+        for (int i = 0; i < 50; ++i) {
+            std::ostringstream ss;
+            ss << GROUP_NAME_CORE << "_" << i;
+            reference_elems.push_back(ss.str());
+        }
+        // there is no guarantee on the order of the hdf5 index, let's sort it
+        // to put them in order
+        std::sort(elems.begin(), elems.end());
+        std::sort(reference_elems.begin(), reference_elems.end());
+
+        BOOST_CHECK_EQUAL_COLLECTIONS(elems.begin(), elems.end(), reference_elems.begin(),
+                                      reference_elems.end());
+    }
+}
+
+BOOST_AUTO_TEST_CASE(DataTypeEqualSimple) {
+    AtomicType<double> d_var;
+    AtomicType<size_t> size_var;
+    AtomicType<double> d_var_test;
+    AtomicType<size_t> size_var_cpy(size_var);
+    AtomicType<int> int_var;
+    AtomicType<unsigned> uint_var;
+
+    // check different type matching
+    BOOST_CHECK(d_var == d_var_test);
+    BOOST_CHECK(d_var != size_var);
+
+    // check type copy matching
+    BOOST_CHECK(size_var_cpy == size_var);
+
+    // check sign change not matching
+    BOOST_CHECK(int_var != uint_var);
+}
+
+BOOST_AUTO_TEST_CASE(DataTypeEqualTakeBack) {
+    const std::string FILE_NAME("h5tutr_dset.h5");
+    const std::string DATASET_NAME("dset");
+
+    // Create a new file using the default property lists.
+    File file(FILE_NAME, File::ReadWrite | File::Create | File::Truncate);
+
+    // Create the data space for the dataset.
+    std::vector<size_t> dims{10, 1};
+
+    DataSpace dataspace(dims);
+
+    // Create a dataset with double precision floating points
+    DataSet dataset = file.createDataSet<size_t>(DATASET_NAME + "_double", dataspace);
+
+    AtomicType<size_t> s;
+    AtomicType<double> d;
+
+    BOOST_CHECK(s == dataset.getDataType());
+    BOOST_CHECK(d != dataset.getDataType());
+
+    // Test getAddress and expect deprecation warning
+    auto addr = dataset.getInfo().getAddress();
+    BOOST_CHECK(addr != 0);
+}
+
+BOOST_AUTO_TEST_CASE(DataSpaceTest) {
+    const std::string FILE_NAME("h5tutr_space.h5");
+    const std::string DATASET_NAME("dset");
+
+    // Create a new file using the default property lists.
+    File file(FILE_NAME, File::ReadWrite | File::Create | File::Truncate);
+
+    // Create the data space for the dataset.
+    DataSpace dataspace{std::vector<size_t>{10, 1}};
+
+    // Create a dataset with size_t type
+    DataSet dataset = file.createDataSet<size_t>(DATASET_NAME, dataspace);
+
+    DataSpace space = dataset.getSpace();
+    DataSpace space2 = dataset.getSpace();
+
+    // verify space id are different
+    BOOST_CHECK_NE(space.getId(), space2.getId());
+
+    // verify space id are consistent
+    BOOST_CHECK_EQUAL(space.getDimensions().size(), 2);
+    BOOST_CHECK_EQUAL(space.getDimensions()[0], 10);
+    BOOST_CHECK_EQUAL(space.getDimensions()[1], 1);
+}
+
+BOOST_AUTO_TEST_CASE(DataSpaceVectorTest) {
+    // Create 1D shortcut dataspace
+    DataSpace space(7);
+
+    BOOST_CHECK_EQUAL(space.getDimensions().size(), 1);
+    BOOST_CHECK_EQUAL(space.getDimensions()[0], 7);
+
+    // Initializer list (explicit)
+    DataSpace space3({8, 9, 10});
+    auto space3_res = space3.getDimensions();
+    std::vector<size_t> space3_ans{8, 9, 10};
+
+    BOOST_CHECK_EQUAL_COLLECTIONS(space3_res.begin(), space3_res.end(),
+                                  space3_ans.begin(), space3_ans.end());
+
+    // Verify 2D works (note that without the {}, this matches the iterator
+    // constructor)
+    DataSpace space2(std::vector<size_t>{3, 4});
+
+    auto space2_res = space2.getDimensions();
+    std::vector<size_t> space2_ans{3, 4};
+
+    BOOST_CHECK_EQUAL_COLLECTIONS(space2_res.begin(), space2_res.end(),
+                                  space2_ans.begin(), space2_ans.end());
+}
+
+BOOST_AUTO_TEST_CASE(DataSpaceVariadicTest) {
+    // Create 1D shortcut dataspace
+    DataSpace space1{7};
+
+    auto space1_res = space1.getDimensions();
+    std::vector<size_t> space1_ans{7};
+
+    BOOST_CHECK_EQUAL_COLLECTIONS(space1_res.begin(), space1_res.end(),
+                                  space1_ans.begin(), space1_ans.end());
+
+    // Initializer list (explicit)
+    DataSpace space3{8, 9, 10};
+
+    auto space3_res = space3.getDimensions();
+    std::vector<size_t> space3_ans{8, 9, 10};
+
+    BOOST_CHECK_EQUAL_COLLECTIONS(space3_res.begin(), space3_res.end(),
+                                  space3_ans.begin(), space3_ans.end());
+
+    // Verify 2D works using explicit syntax
+    DataSpace space2{3, 4};
+
+    auto space2_res = space2.getDimensions();
+    std::vector<size_t> space2_ans{3, 4};
+
+    BOOST_CHECK_EQUAL_COLLECTIONS(space2_res.begin(), space2_res.end(),
+                                  space2_ans.begin(), space2_ans.end());
+
+    // Verify 2D works using old syntax (this used to match the iterator!)
+    DataSpace space2b(3, 4);
+
+    auto space2b_res = space2b.getDimensions();
+    std::vector<size_t> space2b_ans{3, 4};
+
+    BOOST_CHECK_EQUAL_COLLECTIONS(space2b_res.begin(), space2b_res.end(),
+                                  space2b_ans.begin(), space2b_ans.end());
+}
+
+BOOST_AUTO_TEST_CASE(ChunkingConstructorsTest) {
+    Chunking first(1, 2, 3);
+
+    auto first_res = first.getDimensions();
+    std::vector<hsize_t> first_ans{1, 2, 3};
+
+    BOOST_CHECK_EQUAL_COLLECTIONS(first_res.begin(), first_res.end(), first_ans.begin(),
+                                  first_ans.end());
+
+    Chunking second{1, 2, 3};
+
+    auto second_res = second.getDimensions();
+    std::vector<hsize_t> second_ans{1, 2, 3};
+
+    BOOST_CHECK_EQUAL_COLLECTIONS(second_res.begin(), second_res.end(),
+                                  second_ans.begin(), second_ans.end());
+
+    Chunking third({1, 2, 3});
+
+    auto third_res = third.getDimensions();
+    std::vector<hsize_t> third_ans{1, 2, 3};
+
+    BOOST_CHECK_EQUAL_COLLECTIONS(third_res.begin(), third_res.end(), third_ans.begin(),
+                                  third_ans.end());
+}
+
+BOOST_AUTO_TEST_CASE(HighFiveReadWriteShortcut) {
+    std::ostringstream filename;
+    filename << "h5_rw_vec_shortcut_test.h5";
+
+    const unsigned x_size = 800;
+    const std::string DATASET_NAME("dset");
+    std::vector<unsigned> vec;
+    vec.resize(x_size);
+    for (unsigned i = 0; i < x_size; i++)
+        vec[i] = i * 2;
+    std::string at_contents("Contents of string");
+    int my_int = 3;
+    std::vector<std::vector<int>> my_nested = {{1, 2}, {3, 4}};
+
+    // Create a new file using the default property lists.
+    File file(filename.str(), File::ReadWrite | File::Create | File::Truncate);
+
+    // Create a dataset with int points
+    DataSet dataset = file.createDataSet(DATASET_NAME, vec);
+    dataset.createAttribute("str", at_contents);
+
+    DataSet ds_int = file.createDataSet("/TmpInt", my_int);
+    DataSet ds_nested = file.createDataSet("/TmpNest", my_nested);
+
+    std::vector<int> result;
+    dataset.read(result);
+    BOOST_CHECK_EQUAL_COLLECTIONS(vec.begin(), vec.end(), result.begin(), result.end());
+
+    std::string read_in;
+    dataset.getAttribute("str").read(read_in);
+    BOOST_CHECK_EQUAL(read_in, at_contents);
+
+    int out_int = 0;
+    ds_int.read(out_int);
+    BOOST_CHECK_EQUAL(my_int, out_int);
+
+    decltype(my_nested) out_nested;
+    ds_nested.read(out_nested);
+
+    for (size_t i = 0; i < 2; ++i) {
+        for (size_t j = 0; j < 2; ++j) {
+            BOOST_CHECK_EQUAL(my_nested[i][j], out_nested[i][j]);
+        }
+    }
+
+    // Plain c arrays. 1D
+    {
+        int int_c_array[] = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9};
+        DataSet ds_int2 = file.createDataSet("/TmpCArrayInt", int_c_array);
+
+        decltype(int_c_array) int_c_array_out;
+        ds_int2.read(int_c_array_out);
+        for (size_t i = 0; i < 10; ++i) {
+            BOOST_CHECK_EQUAL(int_c_array[i], int_c_array_out[i]);
+        }
+    }
+
+    // Plain c arrays. 2D
+    {
+        char char_c_2darray[][3] = {"aa", "bb", "cc", "12"};
+        DataSet ds_char2 = file.createDataSet("/TmpCArray2dchar", char_c_2darray);
+
+        decltype(char_c_2darray) char_c_2darray_out;
+        ds_char2.read(char_c_2darray_out);
+        for (size_t i = 0; i < 4; ++i) {
+            for (size_t j = 0; j < 3; ++j) {
+                BOOST_CHECK_EQUAL(char_c_2darray[i][j], char_c_2darray_out[i][j]);
+            }
+        }
+    }
+}
+
+template <typename T>
+void readWriteAttributeVectorTest() {
+    std::ostringstream filename;
+    filename << "h5_rw_attribute_vec_" << typeNameHelper<T>() << "_test.h5";
+
+    std::srand((unsigned)std::time(0));
+    const size_t x_size = 25;
+    const std::string DATASET_NAME("dset");
+    typename std::vector<T> vec;
+
+    // Create a new file using the default property lists.
+    File file(filename.str(), File::ReadWrite | File::Create | File::Truncate);
+
+    vec.resize(x_size);
+    ContentGenerate<T> generator;
+    std::generate(vec.begin(), vec.end(), generator);
+
+    {
+        // Create a dummy group to annotate with an attribute
+        Group g = file.createGroup("dummy_group");
+
+        // check that no attributes are there
+        std::size_t n = g.getNumberAttributes();
+        BOOST_CHECK_EQUAL(n, 0);
+
+        std::vector<std::string> all_attribute_names = g.listAttributeNames();
+        BOOST_CHECK_EQUAL(all_attribute_names.size(), 0);
+
+        bool has_attribute = g.hasAttribute("my_attribute");
+        BOOST_CHECK_EQUAL(has_attribute, false);
+
+        Attribute a1 = g.createAttribute<T>("my_attribute", DataSpace::From(vec));
+        a1.write(vec);
+
+        // check now that we effectively have an attribute listable
+        std::size_t n2 = g.getNumberAttributes();
+        BOOST_CHECK_EQUAL(n2, 1);
+
+        has_attribute = g.hasAttribute("my_attribute");
+        BOOST_CHECK(has_attribute);
+
+        all_attribute_names = g.listAttributeNames();
+        BOOST_CHECK_EQUAL(all_attribute_names.size(), 1);
+        BOOST_CHECK_EQUAL(all_attribute_names[0], std::string("my_attribute"));
+
+        // Create the same attribute on a newly created dataset
+        DataSet s = g.createDataSet("dummy_dataset", DataSpace(1), AtomicType<int>());
+
+        Attribute a2 = s.createAttribute<T>("my_attribute_copy", DataSpace::From(vec));
+        a2.write(vec);
+
+        // const data, short-circuit syntax
+        const std::vector<int> v{1, 2, 3};
+        s.createAttribute("version_test", v);
+    }
+
+    {
+        typename std::vector<T> result1, result2;
+
+        Attribute a1_read = file.getGroup("dummy_group").getAttribute("my_attribute");
+        a1_read.read(result1);
+
+        BOOST_CHECK_EQUAL(vec.size(), x_size);
+        BOOST_CHECK_EQUAL(result1.size(), x_size);
+
+        for (size_t i = 0; i < x_size; ++i)
+            BOOST_CHECK_EQUAL(result1[i], vec[i]);
+
+        Attribute a2_read = file.getDataSet("/dummy_group/dummy_dataset")
+                                .getAttribute("my_attribute_copy");
+        a2_read.read(result2);
+
+        BOOST_CHECK_EQUAL(vec.size(), x_size);
+        BOOST_CHECK_EQUAL(result2.size(), x_size);
+
+        for (size_t i = 0; i < x_size; ++i)
+            BOOST_CHECK_EQUAL(result2[i], vec[i]);
+
+        std::vector<int> v;  // with const would print a nice err msg
+        file.getDataSet("/dummy_group/dummy_dataset")
+            .getAttribute("version_test")
+            .read(v);
+    }
+
+    // Delete some attributes
+    {
+        // From group
+        auto g = file.getGroup("dummy_group");
+        g.deleteAttribute("my_attribute");
+        auto n = g.getNumberAttributes();
+        BOOST_CHECK_EQUAL(n, 0);
+
+        // From dataset
+        auto d = file.getDataSet("/dummy_group/dummy_dataset");
+        d.deleteAttribute("my_attribute_copy");
+        n = g.getNumberAttributes();
+        BOOST_CHECK_EQUAL(n, 0);
+    }
+}
+
+BOOST_AUTO_TEST_CASE(ReadWriteAttributeVectorString) {
+    readWriteAttributeVectorTest<std::string>();
+}
+
+BOOST_AUTO_TEST_CASE_TEMPLATE(ReadWriteAttributeVector, T, dataset_test_types) {
+    readWriteAttributeVectorTest<T>();
+}
+
+BOOST_AUTO_TEST_CASE(datasetOffset) {
+    std::string filename = "datasetOffset.h5";
+    std::string dsetname = "dset";
+    const size_t size_dataset = 20;
+
+    File file(filename, File::ReadWrite | File::Create | File::Truncate);
+    std::vector<int> data(size_dataset);
+    DataSet ds = file.createDataSet<int>(dsetname, DataSpace::From(data));
+    ds.write(data);
+    DataSet ds_read = file.getDataSet(dsetname);
+    BOOST_CHECK(ds_read.getOffset() > 0);
+}
+
+template <typename T>
+void selectionArraySimpleTest() {
+    typedef typename std::vector<T> Vector;
+
+    std::ostringstream filename;
+    filename << "h5_rw_select_test_" << typeNameHelper<T>() << "_test.h5";
+
+    const size_t size_x = 10;
+    const size_t offset_x = 2, count_x = 5;
+
+    const std::string DATASET_NAME("dset");
+
+    Vector values(size_x);
+
+    ContentGenerate<T> generator;
+    std::generate(values.begin(), values.end(), generator);
+
+    // Create a new file using the default property lists.
+    File file(filename.str(), File::ReadWrite | File::Create | File::Truncate);
+
+    DataSet dataset = file.createDataSet<T>(DATASET_NAME, DataSpace::From(values));
+
+    dataset.write(values);
+
+    file.flush();
+
+    // select slice
+    {
+        // read it back
+        Vector result;
+        std::vector<size_t> offset{offset_x};
+        std::vector<size_t> size{count_x};
+
+        Selection slice = dataset.select(offset, size);
+
+        BOOST_CHECK_EQUAL(slice.getSpace().getDimensions()[0], size_x);
+        BOOST_CHECK_EQUAL(slice.getMemSpace().getDimensions()[0], count_x);
+
+        slice.read(result);
+
+        BOOST_CHECK_EQUAL(result.size(), 5);
+
+        for (size_t i = 0; i < count_x; ++i) {
+            BOOST_CHECK_EQUAL(values[i + offset_x], result[i]);
+        }
+    }
+
+    // select cherry pick
+    {
+        // read it back
+        Vector result;
+        std::vector<size_t> ids{1, 3, 4, 7};
+
+        Selection slice = dataset.select(ElementSet(ids));
+
+        BOOST_CHECK_EQUAL(slice.getSpace().getDimensions()[0], size_x);
+        BOOST_CHECK_EQUAL(slice.getMemSpace().getDimensions()[0], ids.size());
+
+        slice.read(result);
+
+        BOOST_CHECK_EQUAL(result.size(), ids.size());
+
+        for (size_t i = 0; i < ids.size(); ++i) {
+            const std::size_t id = ids[i];
+            BOOST_CHECK_EQUAL(values[id], result[i]);
+        }
+    }
+}
+
+BOOST_AUTO_TEST_CASE(selectionArraySimpleString) {
+    selectionArraySimpleTest<std::string>();
+}
+
+BOOST_AUTO_TEST_CASE_TEMPLATE(selectionArraySimple, T, dataset_test_types) {
+    selectionArraySimpleTest<T>();
+}
+
+BOOST_AUTO_TEST_CASE(selectionByElementMultiDim) {
+    const std::string FILE_NAME("h5_test_selection_multi_dim.h5");
+    // Create a 2-dim dataset
+    File file(FILE_NAME, File::ReadWrite | File::Create | File::Truncate);
+    std::vector<size_t> dims{3, 3};
+
+    auto set = file.createDataSet("test", DataSpace(dims), AtomicType<int>());
+    int values[3][3] = {{1, 2, 3}, {4, 5, 6}, {7, 8, 9}};
+    set.write(values);
+
+    {
+        int value;
+        set.select(ElementSet{{1, 1}}).read(value);
+        BOOST_CHECK_EQUAL(value, 5);
+    }
+
+    {
+        int value[2];
+        set.select(ElementSet{0, 0, 2, 2}).read(value);
+        BOOST_CHECK_EQUAL(value[0], 1);
+        BOOST_CHECK_EQUAL(value[1], 9);
+    }
+
+    {
+        int value[2];
+        set.select(ElementSet{{0, 1}, {1, 2}}).read(value);
+        BOOST_CHECK_EQUAL(value[0], 2);
+        BOOST_CHECK_EQUAL(value[1], 6);
+    }
+
+    {
+        SilenceHDF5 silencer;
+        BOOST_CHECK_THROW(set.select(ElementSet{0, 1, 2}), DataSpaceException);
+    }
+}
+
+template <typename T>
+void columnSelectionTest() {
+    std::ostringstream filename;
+    filename << "h5_rw_select_column_test_" << typeNameHelper<T>() << "_test.h5";
+
+    const size_t x_size = 10;
+    const size_t y_size = 7;
+
+    const std::string DATASET_NAME("dset");
+
+    T values[x_size][y_size];
+
+    ContentGenerate<T> generator;
+    generate2D(values, x_size, y_size, generator);
+
+    // Create a new file using the default property lists.
+    File file(filename.str(), File::ReadWrite | File::Create | File::Truncate);
+
+    // Create the data space for the dataset.
+    std::vector<size_t> dims{x_size, y_size};
+
+    DataSpace dataspace(dims);
+    // Create a dataset with arbitrary type
+    DataSet dataset = file.createDataSet<T>(DATASET_NAME, dataspace);
+
+    dataset.write(values);
+
+    file.flush();
+
+    std::vector<size_t> columns{1, 3, 5};
+
+    Selection slice = dataset.select(columns);
+    T result[x_size][3];
+    slice.read(result);
+
+    BOOST_CHECK_EQUAL(slice.getSpace().getDimensions()[0], x_size);
+    BOOST_CHECK_EQUAL(slice.getMemSpace().getDimensions()[0], x_size);
+
+    for (size_t i = 0; i < 3; ++i)
+        for (size_t j = 0; j < x_size; ++j)
+            BOOST_CHECK_EQUAL(result[j][i], values[j][columns[i]]);
+}
+
+BOOST_AUTO_TEST_CASE_TEMPLATE(columnSelection, T, numerical_test_types) {
+    columnSelectionTest<T>();
+}
+
+template <typename T>
+void attribute_scalar_rw() {
+    std::ostringstream filename;
+    filename << "h5_rw_attribute_scalar_rw" << typeNameHelper<T>() << "_test.h5";
+
+    File h5file(filename.str(), File::ReadWrite | File::Create | File::Truncate);
+
+    ContentGenerate<T> generator;
+
+    const T attribute_value(generator());
+
+    Group g = h5file.createGroup("metadata");
+
+    bool family_exist = g.hasAttribute("family");
+    BOOST_CHECK(!family_exist);
+
+    // write a scalar attribute
+    {
+        T out(attribute_value);
+        Attribute att = g.createAttribute<T>("family", DataSpace::From(out));
+        att.write(out);
+    }
+
+    h5file.flush();
+
+    // test if attribute exist
+    family_exist = g.hasAttribute("family");
+    BOOST_CHECK(family_exist);
+
+    // read back a scalar attribute
+    {
+        T res;
+        Attribute att = g.getAttribute("family");
+        att.read(res);
+        BOOST_CHECK_EQUAL(res, attribute_value);
+    }
+}
+
+BOOST_AUTO_TEST_CASE_TEMPLATE(attribute_scalar_rw_all, T, dataset_test_types) {
+    attribute_scalar_rw<T>();
+}
+
+BOOST_AUTO_TEST_CASE(attribute_scalar_rw_string) {
+    attribute_scalar_rw<std::string>();
+}
+
+// regression test https://github.com/BlueBrain/HighFive/issues/98
+BOOST_AUTO_TEST_CASE(HighFiveOutofDimension) {
+    std::string filename("h5_rw_reg_zero_dim_test.h5");
+
+    const std::string DATASET_NAME("dset");
+
+    {
+        // Create a new file using the default property lists.
+        File file(filename, File::ReadWrite | File::Create | File::Truncate);
+
+        DataSpace d_null(DataSpace::DataspaceType::datascape_null);
+
+        DataSet d1 = file.createDataSet<double>(DATASET_NAME, d_null);
+
+        file.flush();
+
+        DataSpace recovered_d1 = d1.getSpace();
+
+        auto ndim = recovered_d1.getNumberDimensions();
+        BOOST_CHECK_EQUAL(ndim, 0);
+
+        auto dims = recovered_d1.getDimensions();
+        BOOST_CHECK_EQUAL(dims.size(), 0);
+    }
+}
+
+template <typename T>
+void readWriteShuffleDeflateTest() {
+    std::ostringstream filename;
+    filename << "h5_rw_deflate_" << typeNameHelper<T>() << "_test.h5";
+    const std::string DATASET_NAME("dset");
+    const size_t x_size = 128;
+    const size_t y_size = 32;
+    const size_t x_chunk = 16;
+    const size_t y_chunk = 16;
+
+    const int deflate_level = 9;
+
+    T array[x_size][y_size];
+
+    // write a compressed file
+    {
+        File file(filename.str(), File::ReadWrite | File::Create | File::Truncate);
+
+        // Create the data space for the dataset.
+        std::vector<size_t> dims{x_size, y_size};
+
+        DataSpace dataspace(dims);
+
+        // Use chunking
+        DataSetCreateProps props;
+        props.add(Chunking(std::vector<hsize_t>{x_chunk, y_chunk}));
+
+        // Enable shuffle
+        props.add(Shuffle());
+
+        // Enable deflate
+        props.add(Deflate(deflate_level));
+
+        // Create a dataset with arbitrary type
+        DataSet dataset = file.createDataSet<T>(DATASET_NAME, dataspace, props);
+
+        ContentGenerate<T> generator;
+        generate2D(array, x_size, y_size, generator);
+
+        dataset.write(array);
+
+        file.flush();
+    }
+
+    // read it back
+    {
+        File file_read(filename.str(), File::ReadOnly);
+        DataSet dataset_read = file_read.getDataSet("/" + DATASET_NAME);
+
+        T result[x_size][y_size];
+
+        dataset_read.read(result);
+
+        for (size_t i = 0; i < x_size; ++i) {
+            for (size_t j = 0; i < y_size; ++i) {
+                BOOST_CHECK_EQUAL(result[i][j], array[i][j]);
+            }
+        }
+    }
+}
+
+BOOST_AUTO_TEST_CASE_TEMPLATE(ReadWriteShuffleDeflate, T, numerical_test_types) {
+    readWriteShuffleDeflateTest<T>();
+}
+
+template <typename T>
+void readWriteSzipTest() {
+    std::ostringstream filename;
+    filename << "h5_rw_szip_" << typeNameHelper<T>() << "_test.h5";
+    const std::string DATASET_NAME("dset");
+    const size_t x_size = 128;
+    const size_t y_size = 32;
+    const size_t x_chunk = 8;
+    const size_t y_chunk = 4;
+
+    const int options_mask = H5_SZIP_NN_OPTION_MASK;
+    const int pixels_per_block = 8;
+
+    T array[x_size][y_size];
+
+    // write a compressed file
+    {
+        File file(filename.str(), File::ReadWrite | File::Create | File::Truncate);
+
+        // Create the data space for the dataset.
+        std::vector<size_t> dims{x_size, y_size};
+
+        DataSpace dataspace(dims);
+
+        // Use chunking
+        DataSetCreateProps props;
+        props.add(Chunking(std::vector<hsize_t>{x_chunk, y_chunk}));
+
+        // Enable szip
+        props.add(Szip(options_mask, pixels_per_block));
+
+        // Create a dataset with arbitrary type
+        DataSet dataset = file.createDataSet<T>(DATASET_NAME, dataspace, props);
+
+        ContentGenerate<T> generator;
+        generate2D(array, x_size, y_size, generator);
+
+        dataset.write(array);
+
+        file.flush();
+    }
+
+    // read it back
+    {
+        File file_read(filename.str(), File::ReadOnly);
+        DataSet dataset_read = file_read.getDataSet("/" + DATASET_NAME);
+
+        T result[x_size][y_size];
+
+        dataset_read.read(result);
+
+        for (size_t i = 0; i < x_size; ++i) {
+            for (size_t j = 0; i < y_size; ++i) {
+                BOOST_CHECK_EQUAL(result[i][j], array[i][j]);
+            }
+        }
+    }
+}
+
+BOOST_AUTO_TEST_CASE_TEMPLATE(ReadWriteSzip, T, dataset_test_types) {
+    // SZIP is not consistently available across distributions.
+    if (H5Zfilter_avail(H5Z_FILTER_SZIP)) {
+        readWriteSzipTest<T>();
+    } else {
+        BOOST_CHECK_THROW(readWriteSzipTest<T>(), PropertyException);
+    }
+}
+
+// Broadcasting is supported
+BOOST_AUTO_TEST_CASE(ReadInBroadcastDims) {
+
+    const std::string FILE_NAME("h5_missmatch1_dset.h5");
+    const std::string DATASET_NAME("dset");
+
+    // Create a new file using the default property lists.
+    File file(FILE_NAME, File::ReadWrite | File::Create | File::Truncate);
+
+    // Create the data space for the dataset.
+    std::vector<size_t> dims_a{1, 3};
+    std::vector<size_t> dims_b{3, 1};
+
+    // 1D input / output vectors
+    std::vector<double> some_data{5.0, 6.0, 7.0};
+    std::vector<double> data_a;
+    std::vector<double> data_b;
+
+    DataSpace dataspace_a(dims_a);
+    DataSpace dataspace_b(dims_b);
+
+    // Create a dataset with double precision floating points
+    DataSet dataset_a = file.createDataSet(DATASET_NAME + "_a", dataspace_a,
+                                           AtomicType<double>());
+    DataSet dataset_b = file.createDataSet(DATASET_NAME + "_b", dataspace_b,
+                                           AtomicType<double>());
+
+    dataset_a.write(some_data);
+    dataset_b.write(some_data);
+
+    DataSet out_a = file.getDataSet(DATASET_NAME + "_a");
+    DataSet out_b = file.getDataSet(DATASET_NAME + "_b");
+
+    out_a.read(data_a);
+    out_b.read(data_b);
+
+    BOOST_CHECK_EQUAL_COLLECTIONS(data_a.begin(), data_a.end(), some_data.begin(),
+                                  some_data.end());
+
+    BOOST_CHECK_EQUAL_COLLECTIONS(data_b.begin(), data_b.end(), some_data.begin(),
+                                  some_data.end());
+}
+
+BOOST_AUTO_TEST_CASE(HighFiveRecursiveGroups) {
+    const std::string FILE_NAME("h5_ds_exist.h5");
+    const std::string GROUP_1("group1"), GROUP_2("group2");
+    const std::string DS_PATH = GROUP_1 + "/" + GROUP_2;
+    const std::string DS_NAME = "ds";
+
+    // Create a new file using the default property lists.
+    File file(FILE_NAME, File::ReadWrite | File::Create | File::Truncate);
+
+    BOOST_CHECK_EQUAL(file.getName(), FILE_NAME);
+
+    // Without parents creating both groups will fail
+    {
+        SilenceHDF5 silencer;
+        BOOST_CHECK_THROW(file.createGroup(DS_PATH, false), std::exception);
+    }
+    Group g2 = file.createGroup(DS_PATH);
+
+    std::vector<double> some_data{5.0, 6.0, 7.0};
+    g2.createDataSet(DS_NAME, some_data);
+
+    BOOST_CHECK(file.exist(GROUP_1));
+
+    Group g1 = file.getGroup(GROUP_1);
+    BOOST_CHECK(g1.exist(GROUP_2));
+
+    // checks with full path
+    BOOST_CHECK(file.exist(DS_PATH));
+    BOOST_CHECK(file.exist(DS_PATH + "/" + DS_NAME));
+
+    // Check with wrong middle path (before would raise Exception)
+    BOOST_CHECK_EQUAL(file.exist(std::string("blabla/group2")), false);
+
+    // Using root slash
+    BOOST_CHECK(file.exist(std::string("/") + DS_PATH));
+
+    // Check unlink with existing group
+    BOOST_CHECK(g1.exist(GROUP_2));
+    g1.unlink(GROUP_2);
+    BOOST_CHECK(!g1.exist(GROUP_2));
+
+    // Check unlink with non-existing group
+    {
+        SilenceHDF5 silencer;
+        BOOST_CHECK_THROW(g1.unlink("x"), HighFive::GroupException);
+    }
+}
+
+BOOST_AUTO_TEST_CASE(HighFiveInspect) {
+    const std::string FILE_NAME("group_info.h5");
+    const std::string GROUP_1("group1");
+    const std::string DS_NAME = "ds";
+
+    // Create a new file using the default property lists.
+    File file(FILE_NAME, File::ReadWrite | File::Create | File::Truncate);
+    Group g = file.createGroup(GROUP_1);
+
+    std::vector<double> some_data{5.0, 6.0, 7.0};
+    g.createDataSet(DS_NAME, some_data);
+
+    BOOST_CHECK(file.getLinkType(GROUP_1) == LinkType::Hard);
+
+    {
+        SilenceHDF5 silencer;
+        BOOST_CHECK_THROW(file.getLinkType("x"), HighFive::GroupException);
+    }
+
+    BOOST_CHECK(file.getObjectType(GROUP_1) == ObjectType::Group);
+    BOOST_CHECK(file.getObjectType(GROUP_1 + "/" + DS_NAME) == ObjectType::Dataset);
+    BOOST_CHECK(g.getObjectType(DS_NAME) == ObjectType::Dataset);
+
+    {
+        SilenceHDF5 silencer;
+        BOOST_CHECK_THROW(file.getObjectType(DS_NAME), HighFive::GroupException);
+    }
+
+    // Data type
+    auto ds = g.getDataSet(DS_NAME);
+    auto dt = ds.getDataType();
+    BOOST_CHECK(dt.getClass() == DataTypeClass::Float);
+    BOOST_CHECK(dt.getSize() == 8);
+    BOOST_CHECK(dt.string() == "Float64");
+
+    // meta
+    BOOST_CHECK(ds.getType() == ObjectType::Dataset);  // internal
+    BOOST_CHECK(ds.getInfo().getRefCount() == 1);
+}
+
+
+BOOST_AUTO_TEST_CASE(HighFiveGetPath) {
+
+    File file("getpath.h5", File::ReadWrite | File::Create | File::Truncate);
+
+    int number = 100;
+    Group group = file.createGroup("group");
+    DataSet dataset = group.createDataSet("data", DataSpace(1), AtomicType<int>());
+    dataset.write(number);
+    std::string string_list("Very important DataSet!");
+    Attribute attribute = dataset.createAttribute<std::string>("attribute", DataSpace::From(string_list));
+    attribute.write(string_list);
+
+    BOOST_CHECK_EQUAL("/", file.getPath());
+    BOOST_CHECK_EQUAL("/group", group.getPath());
+    BOOST_CHECK_EQUAL("/group/data", dataset.getPath());
+    BOOST_CHECK_EQUAL("attribute", attribute.getName());
+    BOOST_CHECK_EQUAL("/group/data", attribute.getPath());
+
+    BOOST_CHECK(file == dataset.getFile());
+    BOOST_CHECK(file == attribute.getFile());
+
+    // Destroy file early (it should live inside Dataset/Group)
+    std::unique_ptr<File> f2(new File("getpath.h5"));
+    const auto& d2 = f2->getDataSet("/group/data");
+    f2.reset(nullptr);
+    BOOST_CHECK_EQUAL(d2.getFile().getPath(), "/");
+
+}
+
+BOOST_AUTO_TEST_CASE(HighFiveSoftLinks) {
+    const std::string FILE_NAME("softlinks.h5");
+    const std::string DS_PATH("/hard_link/dataset");
+    const std::string LINK_PATH("/soft_link/to_ds");
+    const std::vector<int> data{11, 22, 33};
+
+    {
+        File file(FILE_NAME, File::ReadWrite | File::Create | File::Truncate);
+        auto dset = file.createDataSet(DS_PATH, data);
+        file.createSoftLink(LINK_PATH, dset);
+    }
+
+    {
+        File file(FILE_NAME, File::ReadWrite);
+        std::vector<int> data_out;
+        file.getDataSet(LINK_PATH).read(data_out);
+        BOOST_CHECK(data == data_out);
+    }
+
+    {
+        const std::string EXTERNAL_LINK_PATH("/external_link/to_ds");
+        File file2("link_external_to.h5", File::ReadWrite | File::Create | File::Truncate);
+        file2.createExternalLink(EXTERNAL_LINK_PATH, FILE_NAME, DS_PATH);
+
+        std::vector<int> data_out;
+        file2.getDataSet(EXTERNAL_LINK_PATH).read(data_out);
+        BOOST_CHECK(data == data_out);
+    }
+}
+
+BOOST_AUTO_TEST_CASE(HighFiveRename) {
+
+    File file("move.h5", File::ReadWrite | File::Create | File::Truncate);
+
+    int number = 100;
+
+    {
+        Group group = file.createGroup("group");
+        DataSet dataset = group.createDataSet("data", DataSpace(1), AtomicType<int>());
+        dataset.write(number);
+        std::string path = dataset.getPath();
+        BOOST_CHECK_EQUAL("/group/data", path);
+    }
+
+    file.rename("/group/data", "/new/group/new/data");
+
+    {
+        DataSet dataset = file.getDataSet("/new/group/new/data");
+        std::string path = dataset.getPath();
+        BOOST_CHECK_EQUAL("/new/group/new/data", path);
+        int read;
+        dataset.read(read);
+        BOOST_CHECK_EQUAL(number, read);
+    }
+}
+
+BOOST_AUTO_TEST_CASE(HighFiveRenameRelative) {
+
+    File file("move.h5", File::ReadWrite | File::Create | File::Truncate);
+    Group group = file.createGroup("group");
+
+    int number = 100;
+
+    {
+        DataSet dataset = group.createDataSet("data", DataSpace(1), AtomicType<int>());
+        dataset.write(number);
+        BOOST_CHECK_EQUAL("/group/data", dataset.getPath());
+    }
+
+    group.rename("data", "new_data");
+
+    {
+        DataSet dataset = group.getDataSet("new_data");
+        BOOST_CHECK_EQUAL("/group/new_data", dataset.getPath());
+        int read;
+        dataset.read(read);
+        BOOST_CHECK_EQUAL(number, read);
+    }
+}
+
+BOOST_AUTO_TEST_CASE(HighFivePropertyObjects) {
+    const auto& plist1 = FileCreateProps::Default();  // get const-ref, otherwise copies
+    BOOST_CHECK_EQUAL(plist1.getId(), H5P_DEFAULT);
+    BOOST_CHECK_EQUAL(plist1.isValid(), false);       // not valid -> no inc_ref
+    auto plist2 = plist1;  // copy  (from Object)
+    BOOST_CHECK_EQUAL(plist2.getId(), H5P_DEFAULT);
+
+    // Underlying object is same (singleton holder of H5P_DEFAULT)
+    const auto& other_plist_type = LinkCreateProps::Default();
+    BOOST_CHECK_EQUAL((void*)&plist1, (void*)&other_plist_type);
+
+    LinkCreateProps plist_g;
+    BOOST_CHECK_EQUAL(plist_g.getId(), H5P_DEFAULT);
+    BOOST_CHECK_EQUAL(plist_g.isValid(), false);
+
+    plist_g.add(CreateIntermediateGroup());
+    BOOST_CHECK(plist_g.isValid());
+    auto plist_g2 = plist_g;
+    BOOST_CHECK(plist_g2.isValid());
+}
+
+
+typedef struct {
+    int m1;
+    int m2;
+    int m3;
+} CSL1;
+
+typedef struct {
+    CSL1 csl1;
+} CSL2;
+
+
+CompoundType create_compound_csl1() {
+    auto t2 = AtomicType<int>();
+    CompoundType t1({{"m1", AtomicType<int>{}}, {"m2", AtomicType<int>{}}, {"m3", t2}});
+
+    return t1;
+}
+
+CompoundType create_compound_csl2() {
+    CompoundType t1 = create_compound_csl1();
+
+    CompoundType t2({{"csl1", t1}});
+
+    return t2;
+}
+
+HIGHFIVE_REGISTER_TYPE(CSL1, create_compound_csl1)
+HIGHFIVE_REGISTER_TYPE(CSL2, create_compound_csl2)
+
+BOOST_AUTO_TEST_CASE(HighFiveCompounds) {
+    const std::string FILE_NAME("compounds_test.h5");
+    const std::string DATASET_NAME1("/a");
+    const std::string DATASET_NAME2("/b");
+
+    File file(FILE_NAME, File::ReadWrite | File::Create | File::Truncate);
+
+    auto t3 = AtomicType<int>();
+    CompoundType t1 = create_compound_csl1();
+    t1.commit(file, "my_type");
+
+    CompoundType t2 = create_compound_csl2();
+    t2.commit(file, "my_type2");
+
+    {  // Not nested
+        auto dataset = file.createDataSet(DATASET_NAME1, DataSpace(2), t1);
+
+        std::vector<CSL1> csl = {{1, 1, 1}, {2, 3, 4}};
+        dataset.write(csl);
+
+        file.flush();
+
+        std::vector<CSL1> result;
+        dataset.select({0}, {2}).read(result);
+
+        BOOST_CHECK_EQUAL(result.size(), 2);
+        BOOST_CHECK_EQUAL(result[0].m1, 1);
+        BOOST_CHECK_EQUAL(result[0].m2, 1);
+        BOOST_CHECK_EQUAL(result[0].m3, 1);
+        BOOST_CHECK_EQUAL(result[1].m1, 2);
+        BOOST_CHECK_EQUAL(result[1].m2, 3);
+        BOOST_CHECK_EQUAL(result[1].m3, 4);
+    }
+
+    {  // Nested
+        auto dataset = file.createDataSet(DATASET_NAME2, DataSpace(2), t2);
+
+        std::vector<CSL2> csl = {{{1, 1, 1}, {2, 3, 4}}};
+        dataset.write(csl);
+
+        file.flush();
+        std::vector<CSL2> result = {{{1, 1, 1}, {2, 3, 4}}};
+        dataset.select({0}, {2}).read(result);
+
+        BOOST_CHECK_EQUAL(result.size(), 2);
+        BOOST_CHECK_EQUAL(result[0].csl1.m1, 1);
+        BOOST_CHECK_EQUAL(result[0].csl1.m2, 1);
+        BOOST_CHECK_EQUAL(result[0].csl1.m3, 1);
+        BOOST_CHECK_EQUAL(result[1].csl1.m1, 2);
+        BOOST_CHECK_EQUAL(result[1].csl1.m2, 3);
+        BOOST_CHECK_EQUAL(result[1].csl1.m3, 4);
+    }
+}
+
+
+struct GrandChild {
+    uint32_t gcm1;
+    uint32_t gcm2;
+    uint32_t gcm3;
+};
+
+struct Child {
+    GrandChild grandChild;
+    uint32_t cm1;
+};
+
+struct Parent {
+    uint32_t pm1;
+    Child child;
+};
+
+CompoundType create_compound_GrandChild() {
+    auto t2 = AtomicType<uint32_t>();
+    CompoundType t1({{"gcm1", AtomicType<uint32_t>{}},
+                     {"gcm2", AtomicType<uint32_t>{}},
+                     {"gcm3", t2,                   }});
+    return t1;
+}
+
+CompoundType create_compound_Child() {
+    auto nestedType = create_compound_GrandChild();
+    return CompoundType{ { { "grandChild", nestedType,           },
+                           { "cm1",   AtomicType<uint32_t>{}} } };
+}
+
+CompoundType create_compound_Parent() {
+    auto nestedType = create_compound_Child();
+    return CompoundType{ { { "pm1",   AtomicType<uint32_t>{}},
+                           { "child", nestedType,           }} };
+}
+
+HIGHFIVE_REGISTER_TYPE(GrandChild, create_compound_GrandChild)
+HIGHFIVE_REGISTER_TYPE(Child,      create_compound_Child)
+HIGHFIVE_REGISTER_TYPE(Parent,     create_compound_Parent)
+
+BOOST_AUTO_TEST_CASE(HighFiveCompoundsNested) {
+    const std::string FILE_NAME("nested_compounds_test.h5");
+    const std::string DATASET_NAME("/a");
+
+    {  // Write
+        File file(FILE_NAME, File::ReadWrite | File::Create | File::Truncate);
+        auto type = create_compound_Parent();
+
+        auto dataset = file.createDataSet(DATASET_NAME, DataSpace(2), type);
+        BOOST_CHECK_EQUAL(dataset.getDataType().getSize(), 20);
+
+        std::vector<Parent> csl = { Parent{ 1, Child{ GrandChild{1,1,1}, 1 } },
+                                    Parent{ 2, Child{ GrandChild{3,4,5}, 6 } } };
+        dataset.write(csl);
+    }
+
+    {  // Read
+        File file(FILE_NAME, File::ReadOnly);
+        std::vector<Parent> result;
+        auto dataset = file.getDataSet(DATASET_NAME);
+        BOOST_CHECK_EQUAL(dataset.getDataType().getSize(), 20);
+        dataset.select({0}, {2}).read(result);
+
+        BOOST_CHECK_EQUAL(result.size(), 2);
+        BOOST_CHECK_EQUAL(result[0].pm1, 1);
+        BOOST_CHECK_EQUAL(result[0].child.grandChild.gcm1, 1);
+        BOOST_CHECK_EQUAL(result[0].child.grandChild.gcm2, 1);
+        BOOST_CHECK_EQUAL(result[0].child.grandChild.gcm3, 1);
+        BOOST_CHECK_EQUAL(result[0].child.cm1, 1);
+        BOOST_CHECK_EQUAL(result[1].pm1, 2);
+        BOOST_CHECK_EQUAL(result[1].child.grandChild.gcm1, 3);
+        BOOST_CHECK_EQUAL(result[1].child.grandChild.gcm2, 4);
+        BOOST_CHECK_EQUAL(result[1].child.grandChild.gcm3, 5);
+        BOOST_CHECK_EQUAL(result[1].child.cm1, 6);
+    }
+}
+
+
+enum Position {
+    FIRST = 1,
+    SECOND = 2,
+    THIRD = 3,
+    LAST = -1,
+};
+
+enum class Direction : signed char {
+    FORWARD = 1,
+    BACKWARD = -1,
+    LEFT = -2,
+    RIGHT = 2,
+};
+
+// This is only for boost test
+std::ostream& operator<<(std::ostream& ost, const Direction& dir) {
+    ost << static_cast<int>(dir);
+    return ost;
+}
+
+EnumType<Position> create_enum_position() {
+    return {{"FIRST", Position::FIRST},
+            {"SECOND", Position::SECOND},
+            {"THIRD", Position::THIRD},
+            {"LAST", Position::LAST}};
+}
+HIGHFIVE_REGISTER_TYPE(Position, create_enum_position)
+
+EnumType<Direction> create_enum_direction() {
+    return {{"FORWARD", Direction::FORWARD},
+            {"BACKWARD", Direction::BACKWARD},
+            {"LEFT", Direction::LEFT},
+            {"RIGHT", Direction::RIGHT}};
+}
+HIGHFIVE_REGISTER_TYPE(Direction, create_enum_direction)
+
+BOOST_AUTO_TEST_CASE(HighFiveEnum) {
+    const std::string FILE_NAME("enum_test.h5");
+    const std::string DATASET_NAME1("/a");
+    const std::string DATASET_NAME2("/b");
+
+    File file(FILE_NAME, File::ReadWrite | File::Create | File::Truncate);
+
+    {  // Unscoped enum
+        auto e1 = create_enum_position();
+        e1.commit(file, "Position");
+
+        auto dataset = file.createDataSet(DATASET_NAME1, DataSpace(1), e1);
+        dataset.write(Position::FIRST);
+
+        file.flush();
+
+        Position result;
+        dataset.select(ElementSet({0})).read(result);
+
+        BOOST_CHECK_EQUAL(result, Position::FIRST);
+    }
+
+    {  // Scoped enum
+        auto e1 = create_enum_direction();
+        e1.commit(file, "Direction");
+
+        auto dataset = file.createDataSet(DATASET_NAME2, DataSpace(5), e1);
+        std::vector<Direction> robot_moves({Direction::BACKWARD, Direction::FORWARD,
+                                            Direction::FORWARD, Direction::LEFT,
+                                            Direction::LEFT});
+        dataset.write(robot_moves);
+
+        file.flush();
+
+        std::vector<Direction> result;
+        dataset.read(result);
+
+        BOOST_CHECK_EQUAL(result[0], Direction::BACKWARD);
+        BOOST_CHECK_EQUAL(result[1], Direction::FORWARD);
+        BOOST_CHECK_EQUAL(result[2], Direction::FORWARD);
+        BOOST_CHECK_EQUAL(result[3], Direction::LEFT);
+        BOOST_CHECK_EQUAL(result[4], Direction::LEFT);
+    }
+}
+
+BOOST_AUTO_TEST_CASE(HighFiveFixedString) {
+    const std::string FILE_NAME("array_atomic_types.h5");
+    const std::string GROUP_1("group1");
+
+    // Create a new file using the default property lists.
+    File file(FILE_NAME, File::ReadWrite | File::Create | File::Truncate);
+    char raw_strings[][10] = {"abcd", "1234"};
+
+    /// This will not compile - only char arrays - hits static_assert with a nice error
+    // file.createDataSet<int[10]>(DS_NAME, DataSpace(2)));
+
+    {  // But char should be fine
+        auto ds = file.createDataSet<char[10]>("ds1", DataSpace(2));
+        BOOST_CHECK(ds.getDataType().getClass() == DataTypeClass::String);
+        ds.write(raw_strings);
+    }
+
+    {  // char[] is, by default, int8
+        auto ds2 = file.createDataSet("ds2", raw_strings);
+        BOOST_CHECK(ds2.getDataType().getClass() == DataTypeClass::Integer);
+    }
+
+    {  // String Truncate happens low-level if well setup
+        auto ds3 = file.createDataSet<char[6]>(
+            "ds3", DataSpace::FromCharArrayStrings(raw_strings));
+        ds3.write(raw_strings);
+    }
+
+    {  // Write as raw elements from pointer (with const)
+        const char(*strings_fixed)[10] = raw_strings;
+        // With a pointer we dont know how many strings -> manual DataSpace
+        file.createDataSet<char[10]>("ds4", DataSpace(2)).write(strings_fixed);
+    }
+
+    {  // Cant convert flex-length to fixed-length
+        const char* buffer[] = {"abcd", "1234"};
+        SilenceHDF5 silencer;
+        BOOST_CHECK_THROW(file.createDataSet<char[10]>("ds5", DataSpace(2)).write(buffer),
+                          HighFive::DataSetException);
+    }
+
+    {  // scalar char strings
+        const char buffer[] = "abcd";
+        file.createDataSet<char[10]>("ds6", DataSpace(1)).write(buffer);
+    }
+
+    {  // Dedicated FixedLenStringArray
+        FixedLenStringArray<10> arr{"0000000", "1111111"};
+        // For completeness, test also the other constructor
+        FixedLenStringArray<10> arrx(std::vector<std::string>{"0000", "1111"});
+
+        // More API: test inserting something
+        arr.push_back("2222");
+        auto ds = file.createDataSet("ds7", arr);  // Short syntax ok
+
+        // Recover truncating
+        FixedLenStringArray<4> array_back;
+        ds.read(array_back);
+        BOOST_CHECK(array_back.size() == 3);
+        BOOST_CHECK(array_back[0] == std::string("000"));
+        BOOST_CHECK(array_back[1] == std::string("111"));
+        BOOST_CHECK(array_back[2] == std::string("222"));
+        BOOST_CHECK(array_back.getString(1) == "111");
+        BOOST_CHECK(array_back.front() == std::string("000"));
+        BOOST_CHECK(array_back.back() == std::string("222"));
+        BOOST_CHECK(array_back.data() == std::string("000"));
+        array_back.data()[0] = 'x';
+        BOOST_CHECK(array_back.data() == std::string("x00"));
+
+        for (auto& raw_elem : array_back) {
+            raw_elem[1] = 'y';
+        }
+        BOOST_CHECK(array_back.getString(1) == "1y1");
+        for (auto iter = array_back.cbegin(); iter != array_back.cend(); ++iter) {
+            BOOST_CHECK((*iter)[1] == 'y');
+        }
+    }
+}
+
+BOOST_AUTO_TEST_CASE(HighFiveFixedLenStringArrayStructure) {
+
+    using fixed_array_t = FixedLenStringArray<10>;
+    // increment the characters of a string written in a std::array
+    auto increment_string = [](const fixed_array_t::value_type arr) {
+        fixed_array_t::value_type output(arr);
+        for (auto& c : output) {
+            if (c == 0) {
+                break;
+            }
+            ++c;
+        }
+        return output;
+    };
+
+    // manipulate FixedLenStringArray with std::copy
+    {
+        const fixed_array_t arr1{"0000000", "1111111"};
+        fixed_array_t arr2{"0000000", "1111111"};
+        std::copy(arr1.begin(), arr1.end(), std::back_inserter(arr2));
+        BOOST_CHECK_EQUAL(arr2.size(), 4);
+    }
+
+    // manipulate FixedLenStringArray with std::transform
+    {
+        fixed_array_t arr;
+        {
+            const fixed_array_t arr1{"0000000", "1111111"};
+            std::transform(arr1.begin(), arr1.end(), std::back_inserter(arr),
+                           increment_string);
+        }
+        BOOST_CHECK_EQUAL(arr.size(), 2);
+        BOOST_CHECK_EQUAL(arr[0], std::string("1111111"));
+        BOOST_CHECK_EQUAL(arr[1], std::string("2222222"));
+    }
+
+    // manipulate FixedLenStringArray with std::transform and reverse iterator
+    {
+        fixed_array_t arr;
+        {
+            const fixed_array_t arr1{"0000000", "1111111"};
+            std::copy(arr1.rbegin(), arr1.rend(), std::back_inserter(arr));
+        }
+        BOOST_CHECK_EQUAL(arr.size(), 2);
+        BOOST_CHECK_EQUAL(arr[0], std::string("1111111"));
+        BOOST_CHECK_EQUAL(arr[1], std::string("0000000"));
+    }
+
+    // manipulate FixedLenStringArray with std::remove_copy_if
+    {
+        fixed_array_t arr2;
+        {
+            const fixed_array_t arr1{"0000000", "1111111"};
+            std::remove_copy_if(arr1.begin(), arr1.end(), std::back_inserter(arr2),
+                                [](const fixed_array_t::value_type& s) {
+                                    return std::strncmp(s.data(), "1111111", 7) == 0;
+                                });
+        }
+        BOOST_CHECK_EQUAL(arr2.size(), 1);
+        BOOST_CHECK_EQUAL(arr2[0], std::string("0000000"));
+    }
+}
+
+
+BOOST_AUTO_TEST_CASE(HighFiveFixedLenStringArrayAttribute) {
+    const std::string FILE_NAME("fixed_array_attr.h5");
+    // Create a new file using the default property lists.
+    {
+        File file(FILE_NAME, File::ReadWrite | File::Create | File::Truncate);
+        FixedLenStringArray<10> arr{"Hello", "world"};
+        file.createAttribute("str", arr);
+    }
+    // Re-read it
+    {
+        File file(FILE_NAME);
+        FixedLenStringArray<8> arr;  // notice the output strings can be smaller
+        file.getAttribute("str").read(arr);
+        BOOST_CHECK_EQUAL(arr.size(), 2);
+        BOOST_CHECK_EQUAL(arr[0], std::string("Hello"));
+        BOOST_CHECK_EQUAL(arr[1], std::string("world"));
+    }
+
+}
+
+
+BOOST_AUTO_TEST_CASE(HighFiveReference) {
+    const std::string FILE_NAME("h5_ref_test.h5");
+    const std::string DATASET1_NAME("dset1");
+    const std::string DATASET2_NAME("dset2");
+    const std::string GROUP_NAME("/group1");
+    const std::string REFGROUP_NAME("/group2");
+    const std::string REFDATASET_NAME("dset2");
+
+    ContentGenerate<double> generator;
+    std::vector<double> vec1(4);
+    std::vector<double> vec2(4);
+    std::generate(vec1.begin(), vec1.end(), generator);
+    std::generate(vec2.begin(), vec2.end(), generator);
+    {
+        // Create a new file using the default property lists.
+        File file(FILE_NAME, File::ReadWrite | File::Create | File::Truncate);
+
+        // create group
+        Group g1 = file.createGroup(GROUP_NAME);
+
+        // create datasets and write some data
+        DataSet dataset1 = g1.createDataSet(DATASET1_NAME, vec1);
+        DataSet dataset2 = g1.createDataSet(DATASET2_NAME, vec2);
+
+        // create group to hold reference
+        Group refgroup = file.createGroup(REFGROUP_NAME);
+
+        // create the references and write them into a new dataset inside refgroup
+        auto references = std::vector<Reference>({{g1, dataset1}, {file, g1}});
+        DataSet ref_ds = refgroup.createDataSet(REFDATASET_NAME, references);
+    }
+    // read it back
+    {
+        File file(FILE_NAME, File::ReadOnly);
+        Group refgroup = file.getGroup(REFGROUP_NAME);
+
+        DataSet refdataset = refgroup.getDataSet(REFDATASET_NAME);
+        BOOST_CHECK_EQUAL(2, refdataset.getSpace().getDimensions()[0]);
+        auto refs = std::vector<Reference>();
+        refdataset.read(refs);
+        BOOST_CHECK_THROW(refs[0].dereference<Group>(file), HighFive::ReferenceException);
+        auto data_ds = refs[0].dereference<DataSet>(file);
+        std::vector<double> rdata;
+        data_ds.read(rdata);
+        for (size_t i = 0; i < rdata.size(); ++i) {
+            BOOST_CHECK_EQUAL(rdata[i], vec1[i]);
+        }
+
+        auto group = refs[1].dereference<Group>(file);
+        DataSet data_ds2 = group.getDataSet(DATASET2_NAME);
+        std::vector<double> rdata2;
+        data_ds2.read(rdata2);
+        for (size_t i = 0; i < rdata2.size(); ++i) {
+            BOOST_CHECK_EQUAL(rdata2[i], vec2[i]);
+        }
+    }
+}
+
+#ifdef H5_USE_EIGEN
+
+template <typename T>
+void test_eigen_vec(File& file,
+                    const std::string& test_flavor,
+                    const T& vec_input,
+                    T& vec_output) {
+    const std::string DS_NAME = "ds";
+    file.createDataSet(DS_NAME + test_flavor, vec_input).write(vec_input);
+    file.getDataSet(DS_NAME + test_flavor).read(vec_output);
+    BOOST_CHECK(vec_input == vec_output);
+}
+
+BOOST_AUTO_TEST_CASE(HighFiveEigen) {
+    const std::string FILE_NAME("test_eigen.h5");
+
+    // Create a new file using the default property lists.
+    File file(FILE_NAME, File::ReadWrite | File::Create | File::Truncate);
+    std::string DS_NAME_FLAVOR;
+
+    // std::vector<of vector <of POD>>
+    {
+        DS_NAME_FLAVOR = "VectorOfVectorOfPOD";
+        std::vector<std::vector<float>> vec_in{
+            {5.0f, 6.0f, 7.0f}, {5.1f, 6.1f, 7.1f}, {5.2f, 6.2f, 7.2f}};
+        std::vector<std::vector<float>> vec_out;
+        test_eigen_vec(file, DS_NAME_FLAVOR, vec_in, vec_out);
+    }
+
+    // std::vector<Eigen::Vector3d>
+    {
+        DS_NAME_FLAVOR = "VectorOfEigenVector3d";
+        std::vector<Eigen::Vector3d> vec_in{{5.0, 6.0, 7.0}, {7.0, 8.0, 9.0}};
+        std::vector<Eigen::Vector3d> vec_out;
+        test_eigen_vec(file, DS_NAME_FLAVOR, vec_in, vec_out);
+    }
+
+    // Eigen Vector2d
+    {
+        DS_NAME_FLAVOR = "EigenVector2d";
+        Eigen::Vector2d vec_in{5.0, 6.0};
+        Eigen::Vector2d vec_out;
+
+        test_eigen_vec(file, DS_NAME_FLAVOR, vec_in, vec_out);
+    }
+
+    // Eigen Matrix
+    {
+        DS_NAME_FLAVOR = "EigenMatrix";
+        Eigen::Matrix<double, 3, 3> vec_in;
+        vec_in << 1, 2, 3, 4, 5, 6, 7, 8, 9;
+        Eigen::Matrix<double, 3, 3> vec_out;
+
+        test_eigen_vec(file, DS_NAME_FLAVOR, vec_in, vec_out);
+    }
+
+    // Eigen MatrixXd
+    {
+        DS_NAME_FLAVOR = "EigenMatrixXd";
+        Eigen::MatrixXd vec_in = 100. * Eigen::MatrixXd::Random(20, 5);
+        Eigen::MatrixXd vec_out(20, 5);
+
+        test_eigen_vec(file, DS_NAME_FLAVOR, vec_in, vec_out);
+    }
+
+    // std::vector<of EigenMatrixXd>
+    {
+        DS_NAME_FLAVOR = "VectorEigenMatrixXd";
+
+        Eigen::MatrixXd m1 = 100. * Eigen::MatrixXd::Random(20, 5);
+        Eigen::MatrixXd m2 = 100. * Eigen::MatrixXd::Random(20, 5);
+        std::vector<Eigen::MatrixXd> vec_in;
+        vec_in.push_back(m1);
+        vec_in.push_back(m2);
+        std::vector<Eigen::MatrixXd> vec_out(2, Eigen::MatrixXd::Zero(20, 5));
+
+        test_eigen_vec(file, DS_NAME_FLAVOR, vec_in, vec_out);
+    }
+
+    // std::vector<of EigenMatrixXd> - exception
+    {
+        const std::string DS_NAME = "ds";
+        DS_NAME_FLAVOR = "VectorEigenMatrixXdExc";
+
+        Eigen::MatrixXd m1 = 100. * Eigen::MatrixXd::Random(20, 5);
+        Eigen::MatrixXd m2 = 100. * Eigen::MatrixXd::Random(20, 5);
+        std::vector<Eigen::MatrixXd> vec_in;
+        vec_in.push_back(m1);
+        vec_in.push_back(m2);
+        file.createDataSet(DS_NAME + DS_NAME_FLAVOR, vec_in).write(vec_in);
+
+        std::vector<Eigen::MatrixXd> vec_out_exception;
+        SilenceHDF5 silencer;
+        BOOST_CHECK_THROW(
+            file.getDataSet(DS_NAME + DS_NAME_FLAVOR).read(vec_out_exception),
+            HighFive::DataSetException);
+    }
+
+#ifdef H5_USE_BOOST
+    // boost::multi_array<of EigenVector3f>
+    {
+        DS_NAME_FLAVOR = "BMultiEigenVector3f";
+
+        boost::multi_array<Eigen::Vector3f, 3> vec_in(boost::extents[3][2][2]);
+        for (int i = 0; i < 3; ++i) {
+            for (int j = 0; j < 2; ++j) {
+                for (int k = 0; k < 2; ++k) {
+                    vec_in[i][j][k] = Eigen::Vector3f::Random(3);
+                }
+            }
+        }
+        boost::multi_array<Eigen::Vector3f, 3> vec_out(boost::extents[3][2][2]);
+
+        test_eigen_vec(file, DS_NAME_FLAVOR, vec_in, vec_out);
+    }
+
+    // boost::multi_array<of EigenMatrixXd>
+    {
+        DS_NAME_FLAVOR = "BMultiEigenMatrixXd";
+
+        boost::multi_array<Eigen::MatrixXd, 3> vec_in(boost::extents[3][2][2]);
+        for (int i = 0; i < 3; ++i) {
+            for (int j = 0; j < 2; ++j) {
+                for (int k = 0; k < 2; ++k) {
+                    vec_in[i][j][k] = Eigen::MatrixXd::Random(3, 3);
+                }
+            }
+        }
+        boost::multi_array<Eigen::MatrixXd, 3> vec_out(boost::extents[3][2][2]);
+        for (int i = 0; i < 3; ++i)
+            for (int j = 0; j < 2; ++j) {
+                for (int k = 0; k < 2; ++k) {
+                    vec_out[i][j][k] = Eigen::MatrixXd::Zero(3, 3);
+                }
+            }
+        test_eigen_vec(file, DS_NAME_FLAVOR, vec_in, vec_out);
+    }
+
+    // boost::mulit_array<of EigenMatrixXd> - exception
+    {
+        const std::string DS_NAME = "ds";
+        DS_NAME_FLAVOR = "BMultiEigenMatrixXdExc";
+
+        boost::multi_array<Eigen::MatrixXd, 3> vec_in(boost::extents[3][2][2]);
+        for (int i = 0; i < 3; ++i) {
+            for (int j = 0; j < 2; ++j) {
+                for (int k = 0; k < 2; ++k) {
+                    vec_in[i][j][k] = Eigen::MatrixXd::Random(3, 3);
+                }
+            }
+        }
+
+        file.createDataSet(DS_NAME + DS_NAME_FLAVOR, vec_in).write(vec_in);
+        boost::multi_array<Eigen::MatrixXd, 3> vec_out_exception(boost::extents[3][2][2]);
+
+        SilenceHDF5 silencer;
+        BOOST_CHECK_THROW(
+            file.getDataSet(DS_NAME + DS_NAME_FLAVOR).read(vec_out_exception),
+            HighFive::DataSetException);
+    }
+
+#endif
+}
+#endif
diff --git a/vendor/highfive/tests/unit/tests_high_five_easy.cpp b/vendor/highfive/tests/unit/tests_high_five_easy.cpp
new file mode 100644
index 00000000..9bf31312
--- /dev/null
+++ b/vendor/highfive/tests/unit/tests_high_five_easy.cpp
@@ -0,0 +1,475 @@
+/*
+ *  Copyright (c), 2017, Adrien Devresse <adrien.devresse@epfl.ch>
+ *
+ *  Distributed under the Boost Software License, Version 1.0.
+ *    (See accompanying file LICENSE_1_0.txt or copy at
+ *          http://www.boost.org/LICENSE_1_0.txt)
+ *
+ */
+
+#include <complex>
+#include <cstdio>
+#include <cstdlib>
+#include <ctime>
+#include <iostream>
+#include <memory>
+#include <random>
+#include <string>
+#include <typeinfo>
+#include <vector>
+
+#include <stdio.h>
+
+#include <highfive/H5Easy.hpp>
+
+#ifdef H5_USE_XTENSOR
+#include <xtensor/xrandom.hpp>
+#include <xtensor/xview.hpp>
+#endif
+
+
+#define BOOST_TEST_MAIN H5EasyTests
+
+
+#include <boost/mpl/list.hpp>
+#include <boost/test/unit_test.hpp>
+
+BOOST_AUTO_TEST_CASE(H5Easy_Compression)
+{
+    {
+        H5Easy::DumpOptions options = H5Easy::DumpOptions(H5Easy::Compression());
+        BOOST_CHECK_EQUAL(options.compress(), true);
+        BOOST_CHECK_EQUAL(options.getCompressionLevel(), 9);
+    }
+
+    {
+        H5Easy::DumpOptions options(H5Easy::Compression(true));
+        BOOST_CHECK_EQUAL(options.compress(), true);
+        BOOST_CHECK_EQUAL(options.getCompressionLevel(), 9);
+    }
+
+    {
+        H5Easy::DumpOptions options(H5Easy::Compression(false));
+        BOOST_CHECK_EQUAL(options.compress(), false);
+        BOOST_CHECK_EQUAL(options.getCompressionLevel(), 0);
+    }
+
+    {
+        H5Easy::DumpOptions options(H5Easy::Compression(8));
+        BOOST_CHECK_EQUAL(options.compress(), true);
+        BOOST_CHECK_EQUAL(options.getCompressionLevel(), 8);
+    }
+}
+
+BOOST_AUTO_TEST_CASE(H5Easy_scalar)
+{
+    H5Easy::File file("test.h5", H5Easy::File::Overwrite);
+
+    double a = 1.2345;
+    int b = 12345;
+    std::string c = "12345";
+
+    H5Easy::dump(file, "/path/to/a", a);
+    H5Easy::dump(file, "/path/to/b", b);
+    H5Easy::dump(file, "/path/to/c", c);
+    H5Easy::dump(file, "/path/to/c", c, H5Easy::DumpMode::Overwrite);
+
+    double a_r = H5Easy::load<double>(file, "/path/to/a");
+    int b_r = H5Easy::load<int>(file, "/path/to/b");
+    std::string c_r = H5Easy::load<std::string>(file, "/path/to/c");
+
+    BOOST_CHECK_EQUAL(a == a_r, true);
+    BOOST_CHECK_EQUAL(b == b_r, true);
+    BOOST_CHECK_EQUAL(c == c_r, true);
+}
+
+BOOST_AUTO_TEST_CASE(H5Easy_vector1d)
+{
+    H5Easy::File file("test.h5", H5Easy::File::Overwrite);
+
+    std::vector<size_t> a = {1,2,3,4,5};
+
+    H5Easy::dump(file, "/path/to/a", a);
+
+    std::vector<size_t> a_r = H5Easy::load<std::vector<size_t>>(file, "/path/to/a");
+
+    BOOST_CHECK_EQUAL(a == a_r, true);
+}
+
+BOOST_AUTO_TEST_CASE(H5Easy_vector2d)
+{
+    H5Easy::File file("test.h5", H5Easy::File::Overwrite);
+
+    std::vector<std::vector<size_t>> a({{0, 1}, {2, 3}, {4, 5}});
+
+    H5Easy::dump(file, "/path/to/a", a);
+
+    decltype(a) a_r = H5Easy::load<decltype(a)>(file, "/path/to/a");
+
+    BOOST_CHECK_EQUAL(a == a_r, true);
+}
+
+BOOST_AUTO_TEST_CASE(H5Easy_vector2d_compression)
+{
+    H5Easy::File file("test.h5", H5Easy::File::Overwrite);
+
+    std::vector<std::vector<size_t>> a({{0, 1}, {2, 3}, {4, 5}});
+
+    H5Easy::dump(file, "/path/to/a", a,
+        H5Easy::DumpOptions(H5Easy::Compression(9)));
+
+    H5Easy::dump(file, "/path/to/a", a,
+        H5Easy::DumpOptions(H5Easy::Compression(), H5Easy::DumpMode::Overwrite));
+
+    decltype(a) a_r = H5Easy::load<decltype(a)>(file, "/path/to/a");
+
+    BOOST_CHECK_EQUAL(a == a_r, true);
+}
+
+BOOST_AUTO_TEST_CASE(H5Easy_vector3d)
+{
+    H5Easy::File file("test.h5", H5Easy::File::Overwrite);
+
+    using type = std::vector<std::vector<std::vector<size_t>>>;
+
+    type a({{{0, 1}, {2, 3}}, {{4, 5}, {6, 7}}, {{8, 9}, {10, 11}}});
+
+    H5Easy::dump(file, "/path/to/a", a);
+
+    type a_r = H5Easy::load<type>(file, "/path/to/a");
+
+    BOOST_CHECK_EQUAL(a == a_r, true);
+}
+
+BOOST_AUTO_TEST_CASE(H5Easy_Attribute_scalar)
+{
+    H5Easy::File file("test.h5", H5Easy::File::Overwrite);
+
+    double a = 1.2345;
+    int b = 12345;
+    std::string c = "12345";
+
+    H5Easy::dump(file, "/path/to/a", a);
+    H5Easy::dumpAttribute(file, "/path/to/a", "a", a);
+    H5Easy::dumpAttribute(file, "/path/to/a", "a", a, H5Easy::DumpMode::Overwrite);
+    H5Easy::dumpAttribute(file, "/path/to/a", "b", b);
+    H5Easy::dumpAttribute(file, "/path/to/a", "c", c);
+
+    double a_r = H5Easy::loadAttribute<double>(file, "/path/to/a", "a");
+    int b_r = H5Easy::loadAttribute<int>(file, "/path/to/a", "b");
+    std::string c_r = H5Easy::loadAttribute<std::string>(file, "/path/to/a", "c");
+
+    BOOST_CHECK_EQUAL(a == a_r, true);
+    BOOST_CHECK_EQUAL(b == b_r, true);
+    BOOST_CHECK_EQUAL(c == c_r, true);
+}
+
+#ifdef H5_USE_XTENSOR
+BOOST_AUTO_TEST_CASE(H5Easy_extend1d)
+{
+    H5Easy::File file("test.h5", H5Easy::File::Overwrite);
+
+    for (size_t i = 0; i < 10; ++i) {
+        H5Easy::dump(file, "/path/to/A", i, {i});
+    }
+
+    xt::xarray<size_t> A = xt::arange<size_t>(10);
+
+    xt::xarray<size_t> A_r = H5Easy::load<xt::xarray<size_t>>(file, "/path/to/A");
+
+    size_t Amax = H5Easy::load<size_t>(file, "/path/to/A", {9});
+
+    BOOST_CHECK_EQUAL(xt::allclose(A, A_r), true);
+    BOOST_CHECK_EQUAL(Amax == 9, true);
+}
+
+BOOST_AUTO_TEST_CASE(H5Easy_extend2d)
+{
+    H5Easy::File file("test.h5", H5Easy::File::Overwrite);
+
+    for (size_t i = 0; i < 10; ++i) {
+        for (size_t j = 0; j < 5; ++j) {
+            H5Easy::dump(file, "/path/to/A", i * 5 + j, {i, j});
+        }
+    }
+
+    xt::xarray<size_t> A = xt::arange<size_t>(10 * 5);
+
+    A.reshape({10, 5});
+
+    xt::xarray<size_t> A_r = H5Easy::load<xt::xarray<size_t>>(file, "/path/to/A");
+
+    size_t Amax = H5Easy::load<size_t>(file, "/path/to/A", {9, 4});
+
+    BOOST_CHECK_EQUAL(xt::allclose(A, A_r), true);
+    BOOST_CHECK_EQUAL(Amax == 49, true);
+}
+
+BOOST_AUTO_TEST_CASE(H5Easy_xtensor)
+{
+    H5Easy::File file("test.h5", H5Easy::File::Overwrite);
+
+    xt::xtensor<double, 2> A = 100. * xt::random::randn<double>({20, 5});
+    xt::xtensor<int, 2> B = A;
+
+    H5Easy::dump(file, "/path/to/A", A);
+    H5Easy::dump(file, "/path/to/B", B);
+
+    xt::xtensor<double, 2> A_r = H5Easy::load<xt::xtensor<double, 2>>(file, "/path/to/A");
+    xt::xtensor<int, 2> B_r = H5Easy::load<xt::xtensor<int, 2>>(file, "/path/to/B");
+
+    BOOST_CHECK_EQUAL(xt::allclose(A, A_r), true);
+    BOOST_CHECK_EQUAL(xt::all(xt::equal(B, B_r)), true);
+}
+
+BOOST_AUTO_TEST_CASE(H5Easy_xarray)
+{
+    H5Easy::File file("test.h5", H5Easy::File::Overwrite);
+
+    xt::xarray<double> A = 100. * xt::random::randn<double>({20, 5});
+    xt::xarray<int> B = A;
+
+    H5Easy::dump(file, "/path/to/A", A);
+    H5Easy::dump(file, "/path/to/B", B);
+
+    xt::xarray<double> A_r = H5Easy::load<xt::xarray<double>>(file, "/path/to/A");
+    xt::xarray<int> B_r = H5Easy::load<xt::xarray<int>>(file, "/path/to/B");
+
+    BOOST_CHECK_EQUAL(xt::allclose(A, A_r), true);
+    BOOST_CHECK_EQUAL(xt::all(xt::equal(B, B_r)), true);
+}
+
+BOOST_AUTO_TEST_CASE(H5Easy_view)
+{
+    H5Easy::File file("test.h5", H5Easy::File::Overwrite);
+
+    xt::xtensor<double, 2> A = 100. * xt::random::randn<double>({20, 5});
+    auto a = xt::view(A, xt::range(0, 10), xt::range(0, 10));
+
+    H5Easy::dump(file, "/path/to/a", a);
+
+    xt::xtensor<double, 2> a_r = H5Easy::load<xt::xtensor<double, 2>>(file, "/path/to/a");
+
+    BOOST_CHECK_EQUAL(xt::allclose(a, a_r), true);
+}
+
+BOOST_AUTO_TEST_CASE(H5Easy_xtensor_compress)
+{
+    H5Easy::File file("test.h5", H5Easy::File::Overwrite);
+
+    xt::xtensor<double, 2> A = 100. * xt::random::randn<double>({20, 5});
+    xt::xtensor<int, 2> B = A;
+
+    H5Easy::dump(file, "/path/to/A", A,
+        H5Easy::DumpOptions(H5Easy::Compression()));
+
+    H5Easy::dump(file, "/path/to/A", A,
+        H5Easy::DumpOptions(H5Easy::Compression(), H5Easy::DumpMode::Overwrite));
+
+    H5Easy::dump(file, "/path/to/B", B,
+        H5Easy::DumpOptions(H5Easy::Compression()));
+
+    xt::xtensor<double, 2> A_r = H5Easy::load<xt::xtensor<double, 2>>(file, "/path/to/A");
+    xt::xtensor<int, 2> B_r = H5Easy::load<xt::xtensor<int, 2>>(file, "/path/to/B");
+
+    BOOST_CHECK_EQUAL(xt::allclose(A, A_r), true);
+    BOOST_CHECK_EQUAL(xt::all(xt::equal(B, B_r)), true);
+}
+
+BOOST_AUTO_TEST_CASE(H5Easy_Attribute_xtensor)
+{
+    H5Easy::File file("test.h5", H5Easy::File::Overwrite);
+
+    xt::xtensor<double, 2> A = 100. * xt::random::randn<double>({20, 5});
+    xt::xtensor<int, 2> B = A;
+
+    H5Easy::dump(file, "/path/to/A", A);
+    H5Easy::dumpAttribute(file, "/path/to/A", "A", A);
+    H5Easy::dumpAttribute(file, "/path/to/A", "B", B);
+
+    xt::xtensor<double, 2> A_r = H5Easy::loadAttribute<xt::xtensor<double, 2>>(file, "/path/to/A", "A");
+    xt::xtensor<int, 2> B_r = H5Easy::loadAttribute<xt::xtensor<int, 2>>(file, "/path/to/A", "B");
+
+    BOOST_CHECK_EQUAL(xt::allclose(A, A_r), true);
+    BOOST_CHECK_EQUAL(xt::all(xt::equal(B, B_r)), true);
+}
+#endif
+
+#ifdef H5_USE_EIGEN
+BOOST_AUTO_TEST_CASE(H5Easy_Eigen_MatrixX)
+{
+    H5Easy::File file("test.h5", H5Easy::File::Overwrite);
+
+    Eigen::MatrixXd A = 100. * Eigen::MatrixXd::Random(20, 5);
+    Eigen::MatrixXi B = A.cast<int>();
+
+    H5Easy::dump(file, "/path/to/A", A);
+    H5Easy::dump(file, "/path/to/B", B);
+
+    Eigen::MatrixXd A_r = H5Easy::load<Eigen::MatrixXd>(file, "/path/to/A");
+    Eigen::MatrixXi B_r = H5Easy::load<Eigen::MatrixXi>(file, "/path/to/B");
+
+    BOOST_CHECK_EQUAL(A.isApprox(A_r), true);
+    BOOST_CHECK_EQUAL(B.isApprox(B_r), true);
+}
+
+BOOST_AUTO_TEST_CASE(H5Easy_Eigen_ArrayXX)
+{
+    H5Easy::File file("test.h5", H5Easy::File::Overwrite);
+
+    Eigen::ArrayXXf A = 100. * Eigen::ArrayXXf::Random(20, 5);
+    Eigen::ArrayXXi B = A.cast<int>();
+
+    H5Easy::dump(file, "/path/to/A", A);
+    H5Easy::dump(file, "/path/to/B", B);
+
+    Eigen::ArrayXXf A_r = H5Easy::load<Eigen::MatrixXf>(file, "/path/to/A");
+    Eigen::ArrayXXi B_r = H5Easy::load<Eigen::MatrixXi>(file, "/path/to/B");
+
+    BOOST_CHECK_EQUAL(A.isApprox(A_r), true);
+    BOOST_CHECK_EQUAL(B.isApprox(B_r), true);
+}
+
+BOOST_AUTO_TEST_CASE(H5Easy_Eigen_ArrayX)
+{
+    H5Easy::File file("test.h5", H5Easy::File::Overwrite);
+
+    Eigen::ArrayXf A = Eigen::ArrayXf::Random(50);
+    Eigen::ArrayXi B = A.cast<int>();
+
+    H5Easy::dump(file, "/path/to/A", A);
+    H5Easy::dump(file, "/path/to/B", B);
+
+    Eigen::ArrayXf A_r = H5Easy::load<Eigen::ArrayXf>(file, "/path/to/A");
+    Eigen::ArrayXi B_r = H5Easy::load<Eigen::ArrayXi>(file, "/path/to/B");
+
+    BOOST_CHECK_EQUAL(A.isApprox(A_r), true);
+    BOOST_CHECK_EQUAL(B.isApprox(B_r), true);
+}
+
+
+BOOST_AUTO_TEST_CASE(H5Easy_Eigen_VectorX)
+{
+    H5Easy::File file("test.h5", H5Easy::File::Overwrite);
+
+    Eigen::VectorXd A = 100. * Eigen::VectorXd::Random(20);
+    Eigen::VectorXi B = A.cast<int>();
+
+    H5Easy::dump(file, "/path/to/A", A);
+    H5Easy::dump(file, "/path/to/B", B);
+
+    Eigen::VectorXd A_r = H5Easy::load<Eigen::VectorXd>(file, "/path/to/A");
+    Eigen::VectorXi B_r = H5Easy::load<Eigen::VectorXi>(file, "/path/to/B");
+
+    BOOST_CHECK_EQUAL(A.isApprox(A_r), true);
+    BOOST_CHECK_EQUAL(B.isApprox(B_r), true);
+}
+
+BOOST_AUTO_TEST_CASE(H5Easy_Eigen_MatrixXRowMajor)
+{
+    typedef Eigen::Matrix<double, Eigen::Dynamic, Eigen::Dynamic, Eigen::RowMajor> MatrixXd;
+    typedef Eigen::Matrix<int   , Eigen::Dynamic, Eigen::Dynamic, Eigen::RowMajor> MatrixXi;
+
+    H5Easy::File file("test.h5", H5Easy::File::Overwrite);
+
+    MatrixXd A = 100. * MatrixXd::Random(20, 5);
+    MatrixXi B = A.cast<int>();
+
+    H5Easy::dump(file, "/path/to/A", A);
+    H5Easy::dump(file, "/path/to/B", B);
+
+    MatrixXd A_r = H5Easy::load<MatrixXd>(file, "/path/to/A");
+    MatrixXi B_r = H5Easy::load<MatrixXi>(file, "/path/to/B");
+
+    BOOST_CHECK_EQUAL(A.isApprox(A_r), true);
+    BOOST_CHECK_EQUAL(B.isApprox(B_r), true);
+}
+
+BOOST_AUTO_TEST_CASE(H5Easy_Eigen_VectorXRowMajor)
+{
+    typedef Eigen::Matrix<double, 1, Eigen::Dynamic, Eigen::RowMajor> VectorXd;
+    typedef Eigen::Matrix<int   , 1, Eigen::Dynamic, Eigen::RowMajor> VectorXi;
+
+    H5Easy::File file("test.h5", H5Easy::File::Overwrite);
+
+    VectorXd A = 100. * VectorXd::Random(20);
+    VectorXi B = A.cast<int>();
+
+    H5Easy::dump(file, "/path/to/A", A);
+    H5Easy::dump(file, "/path/to/B", B);
+
+    VectorXd A_r = H5Easy::load<VectorXd>(file, "/path/to/A");
+    VectorXi B_r = H5Easy::load<VectorXi>(file, "/path/to/B");
+
+    BOOST_CHECK_EQUAL(A.isApprox(A_r), true);
+    BOOST_CHECK_EQUAL(B.isApprox(B_r), true);
+}
+
+BOOST_AUTO_TEST_CASE(H5Easy_Eigen_Map)
+{
+    H5Easy::File file("test.h5", H5Easy::File::Overwrite);
+
+    std::vector<int> A = {1, 2, 3, 4, 5, 6, 7, 8, 9};
+    Eigen::Map<Eigen::VectorXi> mapped_vector(A.data(), static_cast<int>(A.size()));
+
+    H5Easy::dump(file, "/path/to/A", mapped_vector);
+
+    std::vector<int> A_r = H5Easy::load<std::vector<int>>(file, "/path/to/A");
+
+    BOOST_CHECK_EQUAL(A == A_r, true);
+}
+
+BOOST_AUTO_TEST_CASE(H5Easy_Attribute_Eigen_MatrixX)
+{
+    H5Easy::File file("test.h5", H5Easy::File::Overwrite);
+
+    Eigen::MatrixXd A = 100. * Eigen::MatrixXd::Random(20, 5);
+    Eigen::MatrixXi B = A.cast<int>();
+
+    H5Easy::dump(file, "/path/to/A", A);
+    H5Easy::dumpAttribute(file, "/path/to/A", "A", A);
+    H5Easy::dumpAttribute(file, "/path/to/A", "B", B);
+
+    Eigen::MatrixXd A_r = H5Easy::loadAttribute<Eigen::MatrixXd>(file, "/path/to/A", "A");
+    Eigen::MatrixXi B_r = H5Easy::loadAttribute<Eigen::MatrixXi>(file, "/path/to/A", "B");
+
+    BOOST_CHECK_EQUAL(A.isApprox(A_r), true);
+    BOOST_CHECK_EQUAL(B.isApprox(B_r), true);
+}
+#endif
+
+#ifdef H5_USE_OPENCV
+BOOST_AUTO_TEST_CASE(H5Easy_OpenCV_Mat_)
+{
+    H5Easy::File file("test.h5", H5Easy::File::Overwrite);
+
+    using T = typename cv::Mat_<double>;
+
+    T A(3, 4, 0.0);
+    A(0, 0) = 0.0;
+    A(0, 1) = 1.0;
+    A(0, 2) = 2.0;
+    A(0, 3) = 3.0;
+    A(1, 0) = 4.0;
+    A(1, 1) = 5.0;
+    A(1, 2) = 6.0;
+    A(1, 3) = 7.0;
+    A(2, 0) = 8.0;
+    A(2, 1) = 9.0;
+    A(2, 2) = 10.0;
+    A(2, 3) = 11.0;
+
+    H5Easy::dump(file, "/path/to/A", A);
+    H5Easy::dumpAttribute(file, "/path/to/A", "attr", A);
+
+    T A_r = H5Easy::load<T>(file, "/path/to/A");
+    T B_r = H5Easy::loadAttribute<T>(file, "/path/to/A", "attr");
+
+    std::vector<double> a(A.begin(), A.end());
+    std::vector<double> a_r(A_r.begin(), A_r.end());
+    std::vector<double> b_r(A_r.begin(), A_r.end());
+
+    BOOST_CHECK_EQUAL(a == a_r, true);
+    BOOST_CHECK_EQUAL(a == b_r, true);
+}
+#endif
diff --git a/vendor/highfive/tests/unit/tests_high_five_multi_dims.cpp b/vendor/highfive/tests/unit/tests_high_five_multi_dims.cpp
new file mode 100644
index 00000000..f482bbe7
--- /dev/null
+++ b/vendor/highfive/tests/unit/tests_high_five_multi_dims.cpp
@@ -0,0 +1,208 @@
+/*
+ *  Copyright (c), 2017-2019, Blue Brain Project - EPFL
+ *
+ *  Distributed under the Boost Software License, Version 1.0.
+ *    (See accompanying file LICENSE_1_0.txt or copy at
+ *          http://www.boost.org/LICENSE_1_0.txt)
+ *
+ */
+
+#include <string>
+#include <iostream>
+
+#include <highfive/H5DataSet.hpp>
+#include <highfive/H5File.hpp>
+
+
+#ifdef H5_USE_BOOST
+#include <boost/multi_array.hpp>
+#endif
+
+#define BOOST_TEST_MAIN HighFiveTestMultiDims
+#include <boost/test/unit_test.hpp>
+
+#include "tests_high_five.hpp"
+
+using namespace HighFive;
+
+
+/// \brief Test for 2D old-style arrays (T array[x][y])
+template <typename T>
+void readWrite2DArrayTest() {
+    std::ostringstream filename;
+    filename << "h5_rw_2d_array_" << typeNameHelper<T>() << "_test.h5";
+    const std::string DATASET_NAME("dset");
+    const size_t x_size = 100;
+    const size_t y_size = 10;
+
+    // Create a new file using the default property lists.
+    File file(filename.str(), File::ReadWrite | File::Create | File::Truncate);
+
+    // Create the data space for the dataset.
+    std::vector<size_t> dims{x_size, y_size};
+
+    DataSpace dataspace(dims);
+
+    // Create a dataset with arbitrary type
+    DataSet dataset = file.createDataSet<T>(DATASET_NAME, dataspace);
+
+    T array[x_size][y_size];
+
+    ContentGenerate<T> generator;
+    generate2D(array, x_size, y_size, generator);
+
+    dataset.write(array);
+
+    T result[x_size][y_size];
+    dataset.read(result);
+
+    for (size_t i = 0; i < x_size; ++i) {
+        for (size_t j = 0; j < y_size; ++j) {
+            BOOST_CHECK_EQUAL(result[i][j], array[i][j]);
+        }
+    }
+}
+
+BOOST_AUTO_TEST_CASE_TEMPLATE(ReadWrite2DArray, T, numerical_test_types) {
+    readWrite2DArrayTest<T>();
+}
+
+template <typename T>
+void readWriteArrayTest() {
+    const size_t x_size = 200;
+    typename std::array<T, x_size> vec;
+    ContentGenerate<T> generator;
+    std::generate(vec.begin(), vec.end(), generator);
+
+    typename std::array<T, x_size> result;
+    auto dataset = readWriteDataset<T>(vec, result, 1, "std-array");
+
+    BOOST_CHECK_EQUAL_COLLECTIONS(result.begin(), result.end(), vec.begin(), vec.end());
+
+    typename std::array<T, 1> tooSmall;
+    BOOST_CHECK_THROW(dataset.read(tooSmall), DataSpaceException);
+}
+BOOST_AUTO_TEST_CASE_TEMPLATE(readWriteArray, T, numerical_test_types) {
+    readWriteArrayTest<T>();
+}
+
+
+template <typename T, typename VectorSubT>
+void readWriteVectorNDTest(std::vector<VectorSubT>& ndvec,
+                           const std::vector<size_t>& dims) {
+    fillVec(ndvec, dims, ContentGenerate<T>());
+
+    std::vector<VectorSubT> result;
+    readWriteDataset<T>(ndvec, result, dims.size(), "vector");
+
+    BOOST_CHECK(checkLength(result, dims));
+    BOOST_CHECK(ndvec == result);
+}
+
+BOOST_AUTO_TEST_CASE_TEMPLATE(readWritSimpleVector, T, numerical_test_types) {
+    std::vector<T> vec;
+    readWriteVectorNDTest<T>(vec, {50});
+}
+
+BOOST_AUTO_TEST_CASE_TEMPLATE(readWrite2DVector, T, numerical_test_types) {
+    std::vector<std::vector<T>> _2dvec;
+    readWriteVectorNDTest<T>(_2dvec, {10, 8});
+}
+
+BOOST_AUTO_TEST_CASE_TEMPLATE(readWrite3DVector, T, numerical_test_types) {
+    std::vector<std::vector<std::vector<T>>> _3dvec;
+    readWriteVectorNDTest<T>(_3dvec, {10, 5, 4});
+}
+
+BOOST_AUTO_TEST_CASE_TEMPLATE(readWrite4DVector, T, numerical_test_types) {
+    std::vector<std::vector<std::vector<std::vector<T>>>> _4dvec;
+    readWriteVectorNDTest<T>(_4dvec, {5, 4, 3, 2});
+}
+
+
+
+#ifdef H5_USE_BOOST
+
+template <typename T>
+void MultiArray3DTest() {
+    typedef typename boost::multi_array<T, 3> MultiArray;
+
+    std::ostringstream filename;
+    filename << "h5_rw_multiarray_" << typeNameHelper<T>() << "_test.h5";
+
+    const int size_x = 10, size_y = 10, size_z = 10;
+    const std::string DATASET_NAME("dset");
+    MultiArray array(boost::extents[size_x][size_y][size_z]);
+
+    ContentGenerate<T> generator;
+    std::generate(array.data(), array.data() + array.num_elements(), generator);
+
+    // Create a new file using the default property lists.
+    File file(filename.str(), File::ReadWrite | File::Create | File::Truncate);
+
+    DataSet dataset = file.createDataSet<T>(DATASET_NAME,
+                                            DataSpace::From(array));
+
+    dataset.write(array);
+
+    // read it back
+    MultiArray result;
+
+    dataset.read(result);
+
+    for (long i = 0; i < size_x; ++i) {
+        for (long j = 0; j < size_y; ++j) {
+            for (long k = 0; k < size_z; ++k) {
+                BOOST_CHECK_EQUAL(array[i][j][k], result[i][j][k]);
+            }
+        }
+    }
+}
+
+BOOST_AUTO_TEST_CASE_TEMPLATE(MultiArray3D, T, numerical_test_types) {
+    MultiArray3DTest<T>();
+}
+
+template <typename T>
+void ublas_matrix_Test() {
+    using Matrix = boost::numeric::ublas::matrix<T>;
+
+    std::ostringstream filename;
+    filename << "h5_rw_multiarray_" << typeNameHelper<T>() << "_test.h5";
+
+    const size_t size_x = 10, size_y = 10;
+    const std::string DATASET_NAME("dset");
+
+    Matrix mat(size_x, size_y);
+
+    ContentGenerate<T> generator;
+    for (std::size_t i = 0; i < mat.size1(); ++i) {
+        for (std::size_t j = 0; j < mat.size2(); ++j) {
+            mat(i, j) = generator();
+        }
+    }
+
+    // Create a new file using the default property lists.
+    File file(filename.str(), File::ReadWrite | File::Create | File::Truncate);
+
+    DataSet dataset = file.createDataSet<T>(DATASET_NAME, DataSpace::From(mat));
+
+    dataset.write(mat);
+
+    // read it back
+    Matrix result;
+
+    dataset.read(result);
+
+    for (size_t i = 0; i < size_x; ++i) {
+        for (size_t j = 0; j < size_y; ++j) {
+            BOOST_CHECK_EQUAL(mat(i, j), result(i, j));
+        }
+    }
+}
+
+BOOST_AUTO_TEST_CASE_TEMPLATE(ublas_matrix, T, numerical_test_types) {
+    ublas_matrix_Test<T>();
+}
+
+#endif
diff --git a/vendor/highfive/tests/unit/tests_high_five_parallel.cpp b/vendor/highfive/tests/unit/tests_high_five_parallel.cpp
new file mode 100644
index 00000000..ba702a69
--- /dev/null
+++ b/vendor/highfive/tests/unit/tests_high_five_parallel.cpp
@@ -0,0 +1,99 @@
+/*
+ *  Copyright (c), 2017-2019, Blue Brain Project - EPFL
+ *
+ *  Distributed under the Boost Software License, Version 1.0.
+ *    (See accompanying file LICENSE_1_0.txt or copy at
+ *          http://www.boost.org/LICENSE_1_0.txt)
+ *
+ */
+
+#include <cstdlib>
+#include <iostream>
+#include <string>
+#include <typeinfo>
+#include <vector>
+
+#include <highfive/H5File.hpp>
+#include <highfive/H5DataSet.hpp>
+#include <highfive/H5DataSpace.hpp>
+#include <highfive/H5Group.hpp>
+
+#define BOOST_TEST_MAIN HighFiveTestParallel
+#include <boost/test/unit_test.hpp>
+
+#include "tests_high_five.hpp"
+
+using namespace HighFive;
+
+
+static int argc = boost::unit_test::framework::master_test_suite().argc;
+static char** argv = boost::unit_test::framework::master_test_suite().argv;
+
+struct MpiFixture {
+    MpiFixture() { MPI_Init(&argc, &argv); }
+    ~MpiFixture() { MPI_Finalize(); }
+};
+
+BOOST_GLOBAL_FIXTURE(MpiFixture)
+BOOST_GLOBAL_FIXTURE_END
+
+template <typename T>
+void selectionArraySimpleTestParallel() {
+
+    int mpi_rank, mpi_size;
+    MPI_Comm_size(MPI_COMM_WORLD, &mpi_size);
+    MPI_Comm_rank(MPI_COMM_WORLD, &mpi_rank);
+
+    typedef typename std::vector<T> Vector;
+
+    std::ostringstream filename;
+    filename << "h5_rw_select_parallel_test_" << typeNameHelper<T>() << "_test.h5";
+
+    const auto size_x = static_cast<size_t>(mpi_size);
+    const auto offset_x = static_cast<size_t>(mpi_rank);
+    const auto count_x = static_cast<size_t>(mpi_size - mpi_rank);
+
+    const std::string DATASET_NAME("dset");
+
+    Vector values(size_x);
+
+    ContentGenerate<T> generator;
+    std::generate(values.begin(), values.end(), generator);
+
+    // Create a new file using the default property lists.
+    File file(filename.str(), File::ReadWrite | File::Create | File::Truncate,
+              MPIOFileDriver(MPI_COMM_WORLD, MPI_INFO_NULL));
+
+    DataSet dataset =
+        file.createDataSet<T>(DATASET_NAME, DataSpace::From(values));
+
+    dataset.write(values);
+
+    file.flush();
+
+    // read it back
+    Vector result;
+    std::vector<size_t> offset;
+    offset.push_back(offset_x);
+    std::vector<size_t> size;
+    size.push_back(count_x);
+
+    Selection slice = dataset.select(offset, size);
+
+    BOOST_CHECK_EQUAL(slice.getSpace().getDimensions()[0], size_x);
+    BOOST_CHECK_EQUAL(slice.getMemSpace().getDimensions()[0], count_x);
+
+    slice.read(result);
+
+    BOOST_CHECK_EQUAL(result.size(), count_x);
+
+    for (size_t i = offset_x; i < count_x; ++i) {
+        // std::cout << result[i] << " ";
+        BOOST_CHECK_EQUAL(values[i + offset_x], result[i]);
+    }
+}
+
+BOOST_AUTO_TEST_CASE_TEMPLATE(selectionArraySimple, T, numerical_test_types) {
+
+    selectionArraySimpleTestParallel<T>();
+}
diff --git a/vendor/highfive/tests/unit/tests_import_public_headers.cpp b/vendor/highfive/tests/unit/tests_import_public_headers.cpp
new file mode 100644
index 00000000..f9c52f2c
--- /dev/null
+++ b/vendor/highfive/tests/unit/tests_import_public_headers.cpp
@@ -0,0 +1,8 @@
+// This is a compilation test to be sure that public header can be included alone
+
+#include <@PUBLIC_HEADER@>
+
+int main()
+{
+    return 0;
+}